CSRF Explained: Cross-Site Request Forgery Attacks and Defenses
Learn how Cross-Site Request Forgery attacks trick users into executing unwanted actions on authenticated web applications and how to defend against them.
When a Link Forges Your Identity
You are logged into your bank. An attacker sends you a link to a harmless-looking page that contains <img src="https://your-bank.com/transfer?amount=1000&to=attacker">. Your browser, seeing a request to your bank, automatically includes your session cookie. The bank processes the transfer — because it trusts your authenticated session. This is Cross-Site Request Forgery (CSRF): the attack exploits the trust a website has in your browser.
Prerequisites
Before studying CSRF, you should understand:
How CSRF Works
The attack requires three conditions:
The Attack Flow
The victim logs into their banking website and receives a session cookie. The attacker crafts an HTML page containing a form that auto-submits a fund transfer request to the banking site. The victim visits the attacker's page. The victim's browser automatically sends the request with the session cookie. The bank processes the unauthorized transfer, believing it came from the legitimate user.
Types of CSRF Attacks
GET-Based CSRF
The simplest form uses an image tag or link:
<img src="https://bank.com/transfer?to=attacker&amount=1000" width="0" height="0">
POST-Based CSRF
Auto-submitting forms bypass POST-only assumptions:
<form action="https://bank.com/transfer" method="POST" id="csrf-form">
<input type="hidden" name="to" value="attacker">
<input type="hidden" name="amount" value="1000">
</form>
<script>document.getElementById('csrf-form').submit();</script>
JSON-Based CSRF
APIs accepting JSON can be targeted with text/plain encoding:
<form action="https://api.bank.com/transfer" method="POST" enctype="text/plain">
<input name='{"to":"attacker","amount":1000}' value=''>
</form>
CSRF Defenses
CSRF Tokens
The most common defense. The server generates a unique, unpredictable token embedded in forms and validated on each state-changing request:
<form action="/transfer" method="POST">
<input type="hidden" name="csrf_token" value="a1b2c3d4e5f6...">
<input type="text" name="to" required>
<input type="number" name="amount" required>
<button type="submit">Transfer</button>
</form>
On the server side:
const csrf = require('csurf');
const csrfProtection = csrf({ cookie: true });
app.post('/transfer', csrfProtection, (req, res) => {
processTransfer(req.body.to, req.body.amount);
});
SameSite Cookies
Cookies with SameSite=Strict or SameSite=Lax are not sent on cross-origin requests:
res.cookie('session', token, {
httpOnly: true,
secure: true,
sameSite: 'Strict'
});
Origin and Referer Header Validation
app.post('/transfer', (req, res) => {
const origin = req.headers.origin;
const allowedOrigins = ['https://bank.com'];
if (!origin || !allowedOrigins.includes(origin)) {
return res.status(403).send('Forbidden');
}
processTransfer(req.body.to, req.body.amount);
});
Real-World Examples
Netflix CSRF (2006): Attackers could add movies to a user's DVD queue and change account settings by getting users to visit malicious pages while logged into Netflix.
YouTube CSRF (2008): A vulnerability allowed attackers to perform any action on behalf of a logged-in user, including adding videos to favorites and deleting videos.
ING Bank CSRF (2015): Researchers demonstrated that ING Direct's banking application was vulnerable to CSRF, allowing attackers to transfer funds and change account settings.
Common Mistakes
Relying on POST-only protection: Using POST instead of GET does not prevent CSRF — attackers can craft auto-submitting forms.
Using weak tokens: Tokens must be cryptographically random per session. Sequential tokens can be predicted.
Not protecting all endpoints: Forgot password forms, logout actions, and API endpoints must all be protected.
Sharing tokens across subdomains: A CSRF token valid on app.example.com should not work on admin.example.com.
Best Practices
Related Tools
Related Articles
Summary
CSRF exploits the trust a web application has in an authenticated user's browser. The primary defense is CSRF tokens — unique, unpredictable values embedded in forms and validated server-side. Modern SameSite cookie attributes provide additional protection. Always protect all state-changing endpoints and implement defense in depth.