Surfing the Web With Seasurf
TL;DR #
CSRF is all about abused trust. You trust your browser. Your browser trusts your cookies. But attackers? They know how to trick your browser into misbehaving on your behalf.
Break that trust chain—and you’ve just disarmed one of the web’s sneakiest attacks.
This post was inspired by the research paper “Robust Defenses for Cross-Site Request Forgery” by Barth, Jackson, and Mitchell (Stanford University).
Oh, and yeah—I wrote this while munching on cookies. 🍪 Stay safe out there.
Understanding and Defending Against CSRF #
You’ve probably heard of CSRF, that acronym that pops up whenever someone talks about web security checklists. But have you really looked at what makes it dangerous, sneaky, and, at times, a pain to mitigate? Let’s break it down in simple terms, with practical examples, and explore some modern defenses that make CSRF less terrifying.
What’s CSRF? #
Imagine you’re logged into your bank website in one tab. In another tab, you’re reading a spicy blog post that secretly contains a hidden form. That form silently submits a request to your bank’s “transfer money” endpoint—with your cookies attached—transferring ₹10,000 to the attacker’s account.
That’s CSRF: Cross-Site Request Forgery. It tricks your browser into sending an authenticated request to a site without your knowledge.
A Very Basic Example #
Here’s how a malicious form might look:
<form action="https://bank.com/transfer" method="POST">
<input type="hidden" name="to" value="attacker_account">
<input type="hidden" name="amount" value="10000">
</form>
<script>document.forms[0].submit()</script>
If you’re logged into your bank in the same browser, this request may succeed unless the bank has some CSRF protection.
Real Incidents #
Back in the day, even big names like Gmail and PayPal were vulnerable. There was a case where a Gmail account got a malicious filter installed via CSRF, forwarding all emails to the attacker. Ouch.
Login CSRF — The Lesser Known Evil #
Not all CSRF attacks aim to steal or transfer something directly. Some are sneakier—like Login CSRF, a lesser-known but equally devious variation.
Instead of performing harmful actions, Login CSRF silently logs you into a website using the attacker’s credentials. You continue browsing, unaware that you’re now operating inside their session. Everything you do—browsing, uploading, commenting—is linked to them, not you.
Why is this bad? #
Let’s say:
- You’re unknowingly logged into Google with the attacker’s account.
- Every search you make, every video you watch—gets saved to their history.
- They can later review everything you’ve done, as if they were watching a recording of your session.
It’s subtle, it’s invisible, and it’s invasive. You might not lose money—but you definitely lose privacy.
How does this happen? #
If a login form accepts credentials via POST without any CSRF protection (tokens, Referer checks, Origin validation), an attacker can inject a hidden form like:
<form action="https://example.com/login" method="POST">
<input type="hidden" name="username" value="attacker@example.com">
<input type="hidden" name="password" value="notsosecure">
</form>
<script>document.forms[0].submit();</script>
Once submitted, your browser obediently sends the request with the attacker’s credentials—and now you’re inside their account.
So How Do We Defend Ourselves? #
Let’s look at some standard (and not-so-standard) techniques.
1. Secret CSRF Tokens (Anti-CSRF Tokens)
The classic approach:
- Server generates a random token per user/session.
- Token is embedded in forms.
- Server checks token on submission.
<input type="hidden" name="csrf_token" value="random123token">
It’s simple but easy to mess up. Also, login forms are often forgotten because there’s no session before login.
2. Referer Header Checks
Sites can check if a request came from the same site by inspecting the Referer header.
But:
- Some browsers or extensions strip it.
- HTTP requests often don’t carry it reliably.
- It works better over HTTPS, where it’s less likely to be stripped.
In simple terms use strict Referer validation for login forms that submit over HTTPS.
3. Custom Headers with XMLHttpRequest / Fetch
AJAX-based requests can include custom headers like:
fetch("/transfer", {
method: "POST",
headers: {
"X-CSRF-Token": "your_token_here"
}
})
Browsers restrict these cross-origin, making it harder to forge such requests. This method is great—but only works if all your state-changing requests are done via JavaScript.
The Modern Answer: The Origin Header #
Proposed to avoid the privacy concerns around Referer, the Origin header only includes:
Origin: https://yourdomain.com
No path. No query. Just enough to validate the source.
Many browsers now send this with POST requests. It’s a clean, privacy-respecting way to validate requests.
And bonus: attackers can’t just suppress or spoof this easily.
Cookie Overwriting is a Thing Too #
Some clever attackers might try overwriting your session cookie over an insecure (HTTP) connection—even if the site uses Secure cookies. But seems like this was patched in Google Chrome 58, not sure about other browsers at the moment.
If you are interested here’s a stackoverflow discussion: Can a secure cookie be set from an insecure HTTP connection?.
Best Practices #
- Use CSRF tokens (HMAC-bound to session ID if possible).
- Reject state-changing
GETrequests. Stick toPOST,PUT,DELETE, etc. - For login forms, use strict Referer checking or Origin validation over HTTPS.
- Consider frameworks that bake in CSRF protection (like Django, Rails, etc.).
FramebustingandSameSitecookies help with related issues.