Documentation

Cross-Site Request Forgery (CSRF) Attacks

HIGH SEVERITY OWASP TOP 10 SESSION ATTACK

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!

Commonly Misunderstood Attack

CSRF affects any website that relies on cookies for authentication. If you can perform an action while logged in (change password, make purchase, post content), CSRF can make you do it without your knowledge. It's ranked in the OWASP Top 10 and has caused billions in fraudulent transactions.


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...
html<!-- Hidden on evilsite.com -->
<img src="https://yourbank.com/transfer?to=attacker&amount=5000" style="display:none;">
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:

html<!-- Simple image tag attack -->
<img src="https://bank.com/transfer?to=attacker&amount=1000">

Real Example - Router Attack:

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

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

Router Hijacking

If you're logged into your router's admin panel and visit this malicious page, your router password changes without your knowledge!

2. POST-based CSRF

HIGH IMPACT HARDER BUT POSSIBLE

The Vulnerability: POST requests are not immune to CSRF!

Attack Vector:

html<!-- 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="[email protected]">
    </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:

html<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>

CORS Protection

CORS (Cross-Origin Resource Sharing) helps prevent this, but misconfigured CORS policies can still allow it.

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:

Malicious Page
html<!-- 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>
Attack Workflow

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:

html<!-- 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 Email
html<!-- Change victim's email -->
<form action="https://netflix.com/account/change-email" method="POST">
    <input name="email" value="[email protected]">
</form>
Reset Password
<!-- 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:

html<!-- 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:

html<!-- 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:

html<!-- First change email -->
<img src="https://site.com/[email protected]">

<!-- 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:

html<!-- 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>

User Deception

User thinks they're clicking "Free iPad" but actually clicking "Confirm Transfer" on the invisible iframe.

3. CSRF with File Upload

FILE SYSTEM

Attack:

html<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:

pythonfrom flask_wtf.csrf import CSRFProtect

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

# In template
<form method="POST">
    {{ csrf_token() }}
    <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"

MODERN DEFENSE

Modern Defense:

javascript// 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

javascriptsameSite: '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

javascriptsameSite: '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)

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

Best Practice:

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

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:

javascript// 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
javascript// 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
javascript// 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
});

Why This Works

Attackers can't set custom headers in simple form submissions or image tags.

5. Origin and Referer Header Validation

DEFENSE IN DEPTH

Check Request Origin:

javascriptapp.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
});

Limitations

  • Some privacy tools strip Referer header
  • Not foolproof - should be used with other methods

6. User Interaction Requirements

CRITICAL ACTIONS

For Critical Actions:

javascript// 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
});

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
html<!-- test-csrf.html -->
<html>
<body>
    <h1>CSRF Test</h1>
    <form action="https://targetsite.com/change-email" method="POST">
        <input name="email" value="[email protected]">
        <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

bash1. Capture request in Burp Proxy
2. Right-click → Engagement tools → Generate CSRF PoC
3. Test generated HTML in browser

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

Learn More

Layerd AI Cloud WAF automatically validates CSRF tokens and enforces SameSite cookie policies, protecting against CSRF attacks without code changes. Learn more →


Last updated: November 2025