How does CSRF work?
The Short Answer
Cross-Site Request Forgery (CSRF) is an attack that tricks a user's browser into making an unwanted request to a site where they're already authenticated. The attacker exploits the fact that browsers automatically attach cookies (including session cookies) to every request to a domain, regardless of where the request originated.
How the Attack Works
CSRF exploits the trust a website has in the user's browser. If you're logged into your bank and visit a malicious page, that page can trigger requests to your bank — and your browser will helpfully attach your session cookie.
Here's the step-by-step attack flow:
- User logs into bank.com — the browser stores a session cookie
- User visits malicious-site.com in another tab (maybe via a phishing link)
- The malicious page contains a hidden form or image tag that targets bank.com
- The browser sends the request to bank.com and automatically attaches the session cookie
- Bank.com sees a valid session cookie and processes the request as legitimate
- Money is transferred, email is changed, or whatever action the attacker targeted — all without the user's knowledge
Why it works
The browser doesn't care where the request originates — it attaches cookies for the target domain regardless. The server receives a perfectly valid session cookie and has no way to tell the difference between a legitimate request and a forged one.
Attack Examples
CSRF attacks can be surprisingly simple. In the example below, a malicious page contains a hidden form with pre-filled values targeting your bank. The moment the page loads, JavaScript auto-submits the form — and because your browser still has a valid session cookie for bank.com, the bank processes it as if you made the request yourself.
<!-- On malicious-site.com -->
<!-- Hidden form that auto-submits -->
<form action="https://bank.com/transfer" method="POST" id="evil-form">
<input type="hidden" name="to" value="attacker-account" />
<input type="hidden" name="amount" value="10000" />
</form>
<script>
document.getElementById('evil-form').submit();
</script>
Attackers don't even need forms — they can use image tags and hidden iframes to trigger requests silently. An <img> tag with a bank URL as its src fires a GET request the moment the page loads, and a hidden iframe can receive a form submission without the user ever seeing a page navigation. None of these require any clicks or interaction from the victim.
<!-- Even simpler — an image tag triggers a GET request -->
<img src="https://bank.com/transfer?to=attacker&amount=10000" width="0" height="0" />
<!-- Hidden iframe with form -->
<iframe style="display:none" name="csrf-frame"></iframe>
<form action="https://bank.com/change-email" method="POST" target="csrf-frame">
<input type="hidden" name="email" value="attacker@evil.com" />
</form>
<script>document.forms[0].submit();</script>
What the Attacker Cannot Do
Understanding CSRF's limitations helps you understand why certain defenses work:
- The attacker cannot read the response — they can trigger actions but can't see what comes back (same-origin policy blocks this)
- The attacker cannot read or set cookies for another domain — they can only cause the browser to send existing cookies
- The attacker cannot set custom headers on cross-origin requests — this is why custom headers are a defense
- The attacker cannot forge requests that require data only available on the target page (like a CSRF token)
Prevention Methods
There are several proven defenses against CSRF. Modern applications typically use multiple layers (defense in depth).
1. CSRF Tokens (Synchronizer Token Pattern)
The server generates a unique, unpredictable token for each session or form. This token is embedded in the page as a hidden field. On submission, the server verifies the token matches. Since the attacker can't read the target page, they can't know the token.
// Server: Generate token and embed in page
import crypto from 'crypto';
function generateCsrfToken(session: Session): string {
const token = crypto.randomBytes(32).toString('hex');
session.csrfToken = token;
return token;
}
// HTML form includes the token as a hidden field:
// <input type="hidden" name="_csrf" value="a8f2e9..." />
// Server middleware: Validate on state-changing requests
function csrfProtection(req: Request, session: Session): boolean {
if (['GET', 'HEAD', 'OPTIONS'].includes(req.method)) return true;
const token = req.body._csrf || req.headers['x-csrf-token'];
return token === session.csrfToken;
}
2. SameSite Cookie Attribute
The SameSite cookie attribute tells the browser when to include cookies in cross-site requests. When set to lax or strict, the browser simply won't attach the cookie to requests that originate from a different site — which kills the entire CSRF attack vector at the browser level. Pay attention to the three possible values and what each one blocks.
res.cookie('session_id', sessionId, {
httpOnly: true, // Not accessible via JavaScript
secure: true, // Only sent over HTTPS
sameSite: 'lax', // The key CSRF defense
maxAge: 3600000,
});
// SameSite values:
// 'strict' — Cookie NEVER sent on cross-site requests
// 'lax' — Cookie sent on top-level navigations (links)
// but NOT on cross-site POST, iframes, or AJAX
// 'none' — Cookie always sent (old behavior, requires Secure)
Since 2020, Chrome defaults to SameSite=Lax for cookies that don't specify a value. This single change eliminated most CSRF attacks on the modern web.
3. Custom Request Headers
Cross-origin requests from forms and image tags cannot set custom headers. Only JavaScript using fetch() or XMLHttpRequest can — and the same-origin policy blocks cross-origin JavaScript from reading responses. So requiring a custom header proves the request came from your own site.
4. Verify Origin/Referer Headers
Check that the Origin or Referer header matches your domain. Requests from malicious sites will have a different origin. This is a defense-in-depth measure — not sufficient alone since headers can sometimes be stripped.
CSRF vs XSS
These two attacks are often confused but work in opposite directions:
| Aspect | CSRF | XSS |
|---|---|---|
| Exploits | Trust the server has in the user's browser | Trust the user has in the website |
| Attacker's goal | Perform actions as the victim | Steal data or execute code in victim's browser |
| Can read response? | No — just triggers the action blindly | Yes — can read cookies, DOM, everything |
| Prevention | CSRF tokens, SameSite cookies | Input sanitization, CSP, HttpOnly cookies |
Important relationship
If your site has an XSS vulnerability, CSRF protections become useless. An XSS attack can read CSRF tokens from the page and bypass all CSRF defenses. Always fix XSS first.
Why Interviewers Ask This
CSRF is one of the OWASP Top 10 web vulnerabilities. Interviewers ask this to verify you understand how browser cookie behavior can be exploited, and that you know practical prevention strategies. Being able to explain the attack flow step-by-step, name multiple defenses, and compare CSRF with XSS shows solid security awareness.
Quick Revision Cheat Sheet
What is CSRF: Tricking a browser into making authenticated requests to a site the user is logged into
Root cause: Browsers auto-attach cookies to requests regardless of where the request originated
Best defense: CSRF tokens + SameSite=Lax cookies + custom headers (defense in depth)
Safe methods: GET, HEAD, OPTIONS don't need CSRF protection (they should never mutate state)
Modern default: Chrome defaults to SameSite=Lax since 2020, blocking most CSRF attacks automatically