mediumFull Stack EngineerBanking
How does CSRF work, and how do you protect against it in a modern web application?
Posted 18/04/2026
by Mehedy Hasan Ador
Question Details
At a banking company:
> "A user is logged into our app. They visit an attacker's site which has a hidden form that submits a POST to our
> "A user is logged into our app. They visit an attacker's site which has a hidden form that submits a POST to our
/api/transfer endpoint. The browser sends the user's cookies automatically. How do we prevent this?"Suggested Solution
How CSRF Works
<!-- Attacker's site (evil.com) -->
<form action="https://bank.com/api/transfer" method="POST">
<input type="hidden" name="to" value="attacker" />
<input type="hidden" name="amount" value="10000" />
</form>
<script>document.forms[0].submit();</script>
<!-- Browser sends the user's session cookie automatically! -->
Prevention Strategies
1. SameSite Cookie Attribute (Easiest)
Set-Cookie: session=abc123; SameSite=Strict; Secure; HttpOnly
// Strict: Never send with cross-site requests
// Lax: Send with top-level navigation (default in modern browsers)
// None: Always send (requires Secure)
2. CSRF Tokens (Traditional)
// Server: Generate token, store in cookie + send in HTML
app.use(csrf({ cookie: true }));
// Frontend: Read token from cookie, send in header
const token = getCookie("XSRF-TOKEN");
fetch("/api/transfer", {
method: "POST",
headers: { "X-CSRF-Token": token },
body: JSON.stringify(data),
});
// Attacker can't read the cookie (Same-Origin Policy)
// So they can't include the token in their forged request
3. JWT in Authorization Header (Best for SPAs)
// Store JWT in memory (not cookie)
// Send via Authorization header (not auto-attached by browser)
fetch("/api/transfer", {
headers: { Authorization: Bearer ${token} },
});
// Attacker's site can't set custom headers (CORS blocks it)
Comparison
Modern approach: SameSite=Lax + JWT in Authorization header.