Cross-Site Request Forgery (CSRF) Attacks
CSRF attacks exploit the trust a website has in a user's browser. Understanding prevention mechanisms is essential for secure web applications.
Quick Navigation: Understanding CSRF Attack Process ⢠CSRF Prevention Methods ⢠CSRF Prevention Methods Comparison ⢠Best Practices for Implementation
Quick Decision Guide
Quick Protection Guide:
Express.js: Use csurf middleware: app.use(csurf()) then add req.csrfToken() to forms.
SameSite Cookies: Set SameSite=Strict on session cookies - prevents cross-site cookie sending: cookie: { sameSite: 'strict', secure: true, httpOnly: true }
CSRF Token Pattern: 1. Generate token on server: const token = generateToken() 2. Store in session: req.session.csrfToken = token 3. Include in form: <input type="hidden" name="_csrf" value="${token}"> 4. Validate on POST: if (req.body._csrf !== req.session.csrfToken) return 403
Protect: All state-changing requests (POST, PUT, DELETE). GET requests are generally safe but validate if they change state.
Common Mistake: Only protecting some endpoints - protect ALL endpoints that modify data.
Understanding CSRF Attack Process
Overview of CSRF Attacks
CSRF (Cross-Site Request Forgery) is an attack that forces authenticated users to execute unwanted actions on a web application. The attack exploits the browser's automatic cookie-sending behavior.
How CSRF Works
Figure 1: CSRF Attack Flow
āāāāāāāāāāāāāāā āāāāāāāāāāāāāāāā āāāāāāāāāāāāāāā
ā User ā ā Attacker ā ā Bank.com ā
ā Browser ā ā Website ā ā Server ā
āāāāāāāā¬āāāāāāā āāāāāāāā¬āāāāāāāā āāāāāāāā¬āāāāāāā
ā ā ā
ā 1. Login ā ā
āāāāāāāāāāāāāāāāāāāāāāāā> ā
ā ā ā
ā <āāāāāāāāāāāāāāāāāāāāāāā Session Cookie ā
ā ā ā
ā 2. Visit ā ā
āāāāāāāāāāāāāāāāāāāāāāāā> ā
ā ā ā
ā 3. Malicious Form ā ā
ā <āāāāāāāāāāāāāāāāāāāāāāā ā
ā ā ā
ā 4. Auto-submit ā ā
ā (with cookies) ā ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā>
ā ā ā
ā ā ā 5. Process
ā ā ā Request
ā ā ā
ā <āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā ā ā
ā 6. Money Transferred ā ā
ā ā āAttack Steps Explained
Step 1: User Authentication
Step 2: User Visits Malicious Site
Step 3: Malicious Site Triggers Request
Step 4: Browser Sends Request
Step 5: Unwanted Action Executed
CSRF Prevention Methods
1. CSRF Tokens
How it works:
Figure 2: CSRF Token Protection Flow
āāāāāāāāāāāāāāā āāāāāāāāāāāāāāāā āāāāāāāāāāāāāāā
ā User ā ā Attacker ā ā Bank.com ā
ā Browser ā ā Website ā ā Server ā
āāāāāāāā¬āāāāāāā āāāāāāāā¬āāāāāāāā āāāāāāāā¬āāāāāāā
ā ā ā
ā 1. Request Form ā ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā>
ā ā ā
ā <āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā Form + CSRF Token ā ā
ā (abc123xyz) ā ā
ā ā ā
ā 2. Submit Form ā ā
ā (with token) ā ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā>
ā ā ā
ā ā 3. Try to submit ā
ā ā (no token) ā
ā āāāāāāāāāāāāāāāāāāāāāāāāāā>
ā ā ā
ā ā <āāāāāāāāāāāāāāāāāāāāāāā
ā ā 403 Forbidden ā
ā ā ā
ā <āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā 200 Success ā ā
ā ā āImplementation:
// Generate token
const token = generateRandomToken();
session.csrfToken = token;
// Include in form (template example)
<form>
<input type="hidden" name="_csrf" value="[TOKEN_VALUE]" />
</form>
// Validate on server
if (req.body._csrf !== session.csrfToken) {
return res.status(403).json({ error: 'Invalid CSRF token' });
}2. SameSite Cookie Attribute
How it works:
Figure 3: SameSite Cookie Protection
āāāāāāāāāāāāāāā āāāāāāāāāāāāāāāā āāāāāāāāāāāāāāā
ā User ā ā Attacker ā ā Bank.com ā
ā Browser ā ā Website ā ā Server ā
āāāāāāāā¬āāāāāāā āāāāāāāā¬āāāāāāāā āāāāāāāā¬āāāāāāā
ā ā ā
ā 1. Login ā ā
āāāāāāāāāāāāāāāāāāāāāāāā> ā
ā ā ā
ā <āāāāāāāāāāāāāāāāāāāāāāā Cookie ā
ā (SameSite=Strict) ā ā
ā ā ā
ā 2. Visit ā ā
āāāāāāāāāāāāāāāāāāāāāāāā> ā
ā ā ā
ā 3. Try CSRF Request ā ā
ā āāāāāāāāāāāāāāāāāāāāāāāāāā>
ā ā (No Cookie Sent) ā
ā ā ā
ā ā <āāāāāāāāāāāāāāāāāāāāāāā
ā ā 401 Unauthorized ā
ā ā āImplementation:
// Set cookie with SameSite
res.cookie('session', token, {
httpOnly: true,
secure: true,
sameSite: 'strict'
});3. Double Submit Cookie
How it works:
Implementation:
// Set cookie with random value
const token = generateRandomToken();
res.cookie('csrf-token', token, { httpOnly: false });
// Include same value in form
<form>
<input type="hidden" name="_csrf" value="[TOKEN_VALUE]" />
</form>
// Validate: cookie must match form value
if (req.cookies['csrf-token'] !== req.body._csrf) {
return res.status(403).json({ error: 'Invalid CSRF token' });
}CSRF Prevention Methods Comparison
| Method | Security Level | Complexity | Browser Support | Use Case |
|---|---|---|---|---|
| CSRF Tokens | āāāāā Highest | Medium | All browsers | Most reliable, works everywhere |
| SameSite Cookies | āāāā High | Low | Modern browsers | Easy to implement, good default |
| Double Submit Cookie | āāā Medium | Low | All browsers | Simple apps, less secure |
Best Practices for Implementation
Start with SameSite Cookies
Initial Setup:
SameSite=Strict on all session cookiessecure flag in production (HTTPS only)httpOnly flag to prevent JavaScript accessExample:
app.use(session({
cookie: {
sameSite: 'strict',
secure: process.env.NODE_ENV === 'production',
httpOnly: true
}
}));Add CSRF Tokens for Critical Operations
For sensitive endpoints:
Express.js Example:
const csrf = require('csurf');
const csrfProtection = csrf({ cookie: true });
// Apply to all POST/PUT/DELETE routes
app.use(csrfProtection);
app.get('/form', (req, res) => {
res.render('form', { csrfToken: req.csrfToken() });
});
app.post('/transfer', csrfProtection, (req, res) => {
// Token automatically validated
// Process transfer...
});Protect All State-Changing Operations
Always protect:
Generally safe (but validate if they change state):
Common Mistakes to Avoid
Mistake 1: Only protecting some endpoints
Mistake 2: Using GET for state changes
Mistake 3: Storing tokens in localStorage
Mistake 4: Not regenerating tokens