How CSRF Exploits Browser Trust
Cross-Site Request Forgery: When the Browser Becomes the Attacker's Tool
Cross-Site Request Forgery (CSRF) is an attack that tricks a logged-in browser into submitting an unwanted state-changing request to a vulnerable application. The attack exploits the browser's automatic inclusion of cookies (including session cookies) with every request to the target origin.
The Attack Mechanism
When a user logs into a web application, the server issues a session cookie. The browser automatically includes this cookie with every subsequent request to that application's origin. An attacker can abuse this behavior by crafting a request that the victim's browser will send with the session cookie attached.
Attack flow:
- User logs into
https://bank.comand receives a session cookie - User visits
https://evil.comin another tab evil.comcontains a form that auto-submits tohttps://bank.com/transfer- The browser sends the request with the user's session cookie
- The bank server processes the transfer, believing it was an intentional user action
Malicious HTML on evil.com:
<form action="https://bank.com/transfer" method="POST" id="csrf-form">
<input type="hidden" name="to" value="attacker-account">
<input type="hidden" name="amount" value="10000">
</form>
<script>document.getElementById('csrf-form').submit();</script>
The victim never sees anything — the form submits silently in the background.
Why CSRF Works
CSRF succeeds because the server cannot distinguish between:
- A request the user intentionally submitted (clicking a button on the bank's site)
- A request forged by an attacker (auto-submitted from evil.com)
Both requests contain the same session cookie. The server has no way to verify that the request originated from the application's own UI.
What CSRF Can and Cannot Do
CSRF can:
- Perform state-changing actions (transfer funds, change email, delete account, modify permissions)
- Act with the full privileges of the authenticated user
- Bypass same-origin policy for the request itself (the browser sends it)
CSRF cannot:
- Read the response (the attacker's page cannot read cross-origin responses due to same-origin policy)
- Steal the session cookie directly (unless combined with XSS)
- Access data that requires additional authentication factors (if MFA is enforced)
Vulnerable HTTP Methods
CSRF primarily targets state-changing methods:
- POST: Most common target — form submissions, API calls
- PUT: REST API updates
- DELETE: REST API deletions
- PATCH: Partial updates
GET requests can also be exploited (e.g., <img src="https://bank.com/transfer?to=attacker&amount=10000">), which is why state-changing actions should never use GET.
Anti-CSRF Tokens and SameSite Cookies
Defending Against CSRF with Tokens and Cookie Attributes
The primary defense against CSRF is the anti-CSRF token — a unique, unpredictable value that the server requires for every state-changing request. The token forces the attacker to read a value they cannot forge.
How Anti-CSRF Tokens Work
Token generation:
When the user authenticates, the server generates a cryptographically random token and associates it with the user's session:
import secrets
@app.route('/login', methods=['POST'])
def login():
# ... validate credentials ...
session['csrf_token'] = secrets.token_urlsafe(32)
return redirect('/dashboard')
Token embedding:
The token is embedded in every form and AJAX request:
<form action="/transfer" method="POST">
<input type="hidden" name="csrf_token" value="{{ session.csrf_token }}">
<input type="text" name="to">
<input type="text" name="amount">
<button type="submit">Transfer</button>
</form>
Token validation:
The server validates the token on every state-changing request:
@app.route('/transfer', methods=['POST'])
def transfer():
submitted_token = request.form.get('csrf_token')
if not secrets.compare_digest(submitted_token, session.get('csrf_token', '')):
return 'CSRF token mismatch', 403
# ... process transfer ...
Why Tokens Defeat CSRF
The attacker on evil.com can submit a form to bank.com, but they cannot read the response from bank.com due to the same-origin policy. Therefore, they cannot obtain the CSRF token. Without the token, the server rejects the request.
Synchronizer Token Pattern vs. Double-Submit Cookie
Synchronizer Token Pattern (server-side):
- Token stored in server-side session
- Token embedded in forms
- Server compares submitted token with session token
- Most secure — token never leaves the server except in forms
Double-Submit Cookie:
- Token stored in a cookie AND submitted in the request body/header
- Server verifies that the cookie value matches the submitted value
- Works without server-side session storage
- Slightly less secure — if an attacker can set cookies on the target origin (via subdomain takeover), they can forge the token
SameSite Cookie Attributes
The SameSite cookie attribute provides a second layer of CSRF defense by controlling when cookies are sent with cross-site requests.
SameSite=Strict:
- Cookie is never sent with cross-site requests
- Strongest CSRF protection
- Downside: user clicking a link from email arrives without a session cookie (must log in again)
SameSite=Lax (default in modern browsers):
- Cookie is sent with top-level GET requests (e.g., clicking a link)
- Cookie is NOT sent with cross-site POST, PUT, DELETE, or iframe requests
- Balances security and usability
SameSite=None:
- Cookie is sent with all cross-site requests
- Requires
Secureflag (HTTPS only) - Effectively disables CSRF protection for that cookie
Defense-in-Depth Strategy
Use multiple layers:
- Anti-CSRF tokens — primary defense for all state-changing operations
- SameSite=Lax cookies — blocks cross-site POST requests from carrying credentials
- Custom request headers — require a header like
X-Requested-With: XMLHttpRequestfor AJAX requests (cross-origin requests cannot set custom headers without CORS) - Re-authentication for sensitive actions — require password re-entry or MFA for high-risk operations (fund transfers, password changes)
Practice: Implement CSRF Protection
Add CSRF protection to this Express.js application:
const express = require('express');
const cookieParser = require('cookie-parser');
const csrf = require('csurf');
const app = express();
app.use(cookieParser());
// Add CSRF protection
const csrfProtection = csrf({ cookie: true });
app.use(csrfProtection);
// Expose token to templates
app.use((req, res, next) => {
res.locals.csrfToken = req.csrfToken();
next();
});
// In your template:
// <form action="/transfer" method="POST">
// <input type="hidden" name="_csrf" value="{{csrfToken}}">
// ...
// </form>
The csurf middleware automatically validates the token on all state-changing requests (POST, PUT, DELETE, PATCH) and rejects requests with missing or invalid tokens.
Practice Problems
A web application allows users to change their email address via a POST request to `/api/change-email`. The endpoint checks the session cookie for authentication but has no CSRF protection. Describe how an attacker could exploit this and what the fix should be.
Solution
An attacker creates a page with a form that auto-submits to `https://app.com/api/change-email` with a hidden field setting the email to the attacker's address. When a logged-in user visits the page, the browser sends the request with the user's session cookie, changing the email. The attacker can then use the password reset flow to take over the account.
Fix: Implement anti-CSRF tokens. Generate a token on login, embed it in the form, and validate it on the server for every state-changing request. Additionally, set `SameSite=Lax` on the session cookie to prevent cross-site POST requests from carrying credentials. A web application sets its session cookie with `SameSite=Strict`. Users report that clicking a login link from their email logs them out. Explain why this happens and suggest a better configuration.
Solution
With `SameSite=Strict`, the browser does not send the session cookie with any cross-site request, including top-level GET requests (clicking a link). When a user clicks a login link from email, the request to the application is cross-site (originating from the email client), so the session cookie is not sent. The application sees an unauthenticated request and redirects to login.
Better configuration: Use `SameSite=Lax`. This sends the cookie with top-level GET requests (preserving the email link experience) but does not send it with cross-site POST/PUT/DELETE requests (maintaining CSRF protection for state-changing operations). For state-changing operations, also implement anti-CSRF tokens as the primary defense. Quiz
1. Why does CSRF succeed even though the attacker cannot read the response from the target application?
2. What is the primary defense against CSRF and why does it work?
3. Which SameSite cookie value provides the best balance between CSRF protection and usability for a typical web application?
4. An attacker crafts an image tag: `<img src="https://bank.com/transfer?to=attacker&amount=10000">`. Which HTTP method is being exploited and why is this dangerous?
5. Which of the following is NOT a valid CSRF defense?
Flashcards
Question
What is CSRF and how does it work?
Click to reveal answer
Answer
Cross-Site Request Forgery tricks a logged-in browser into submitting an unwanted state-changing request. The browser automatically includes the session cookie, so the server processes the forged request as a legitimate user action.
Question
Why can't an attacker read the CSRF token from the target application?
Click to reveal answer
Answer
The same-origin policy prevents cross-origin reads. The attacker's page on evil.com cannot read responses from bank.com, so it cannot obtain the CSRF token needed to forge a valid request.
Question
What is the difference between SameSite=Strict and SameSite=Lax?
Click to reveal answer
Answer
Strict never sends the cookie with cross-site requests (including top-level GET). Lax sends the cookie with top-level GET requests (e.g., clicking a link) but not with cross-site POST/PUT/DELETE. Lax provides better usability while maintaining CSRF protection for state-changing operations.
Question
Why should state-changing operations never use GET?
Click to reveal answer
Answer
GET requests can be triggered by simple HTML elements (img tags, links) without user interaction. If a state-changing operation uses GET, an attacker can forge it with a single image tag. State-changing operations should use POST/PUT/DELETE with CSRF protection.
Question
What is the Synchronizer Token Pattern?
Click to reveal answer
Answer
A CSRF defense where the server generates a cryptographically random token, stores it in the user's session, embeds it in forms, and validates it on every state-changing request. The token never leaves the server except in forms, making it the most secure CSRF defense.
Revision Notes
Key Takeaways
- 1. CSRF exploits the browser's automatic inclusion of session cookies with every request to the target origin
- 2. Anti-CSRF tokens are the primary defense — the attacker cannot read the token due to same-origin policy
- 3. SameSite=Lax provides the best balance between CSRF protection and usability
- 4. State-changing operations must use POST/PUT/DELETE with CSRF protection, never GET
- 5. Defense in depth: tokens + SameSite cookies + custom headers + re-authentication for sensitive actions
Interview Tips
- • Walk through a concrete CSRF attack step-by-step, from the malicious form to the server processing the forged request
- • Explain why anti-CSRF tokens work — focus on the same-origin policy preventing the attacker from reading the token
- • Discuss the trade-offs between SameSite=Strict and SameSite=Lax in terms of usability and security
- • Connect CSRF to the broader web security landscape — mention how it differs from XSS (CSRF exploits browser trust, XSS exploits output encoding failures)
Cheat Sheet
CSRF Protection Cheat Sheet
Phase: Web Vulnerabilities and the OWASP Top 10 | Level: intermediate
Attack Mechanism:
- Attacker crafts a form/request on evil.com targeting the victim's authenticated application
- Browser automatically includes session cookie with the forged request
- Server processes the request as a legitimate user action
Defense Layers:
- Anti-CSRF tokens (primary) — synchronizer token pattern or double-submit cookie
- SameSite=Lax cookies — blocks cross-site POST/PUT/DELETE from carrying credentials
- Custom request headers — cross-origin requests cannot set custom headers without CORS
- Re-authentication for sensitive actions — password re-entry or MFA
SameSite Values:
- Strict: never send cookie cross-site (breaks email links)
- Lax: send with top-level GET only (recommended default)
- None: always send (requires Secure flag, disables CSRF protection)
Key points:
- CSRF tricks a logged-in browser into submitting an unwanted state-changing request
- Per-session anti-CSRF tokens force attackers to read a value they cannot forge
- SameSite cookie attributes block cross-site request bodies from carrying credentials
- State-changing operations must never use GET
Tags: csrf, web-security
Continue Learning
Before You Start
- No prerequisites listed.