The Three Ingredients Of A Csrf Attack
Every CSRF attack, no matter how it's delivered, needs exactly three things to line up at once. Take out any one of them and there's nothing left to exploit.
The last post walked through five real attacks against companies that should have known better. What all five have in common is a much shorter list than five separate stories makes it look.
Three Things Have to Be True at Once
Strip away the specifics of Twitter, Netflix, and TikTok, and every CSRF attack reduces to the same three ingredients.
- A state-changing action worth triggering. Deleting an account, transferring money, posting on someone's behalf. Something that matters if it happens without permission.
- A session that rides on a cookie the browser attaches automatically. The victim has to already be logged in, and the browser has to be willing to hand that cookie over without being asked.
- No unpredictable parameter guarding the request. Nothing the attacker would need to know in advance that they can't already see or guess.
Take out any single one of these and the whole attack collapses. No worthwhile action, and there's nothing to gain from forging a request. No auto-attached cookie, and the forged request arrives with no identity behind it. No missing entropy, and the attacker's forged request gets rejected the instant it's checked.
ExpandThe three ingredients a CSRF attack needs at once: a worthwhile action, a cookie that rides along automatically, and no unpredictable value guarding the request.
That third ingredient is quietly doing most of the work in this list. It's also exactly where the eventual fix lives, though the shape of that fix is worth its own post rather than a rushed mention here.
It helps to picture what removing each one actually looks like in practice. A "view profile" page has no destructive action to trigger, so forging a request at it accomplishes nothing. An endpoint guarded by an API key the browser never auto-attaches means the forged request shows up with no credentials at all. An endpoint that demands a fresh, unpredictable value on every submission is the only one of the three that a defense can realistically be built around, since the other two are just describing what it means for an app to be useful in the first place.
No JavaScript Injection Required
Here's the part that catches people off guard the first time they see it: CSRF needs zero code running on the target site.
Compare that to cross-site scripting, a different family of attack this course covers later, where the whole premise is getting malicious script to execute in the context of the site you're attacking. CSRF doesn't bother with any of that.
The attacker only needs the victim's browser to visit a page they fully control. That page can be anywhere, hosted on any domain, built with any tooling. It doesn't need to inject anything into the bank's site, the social network's site, or wherever the real target lives. It just needs to get a request sent there.
How the Delivery Actually Works
The simplest version doesn't even need a script tag. A GET-based endpoint that changes state can be triggered by something as plain as an image reference.
<img src="https://bank.example.com/transfer?to=attacker&amount=500" />The browser tries to load that as an image, fails quietly, and never shows anything. The request still goes out, cookie attached, exactly as if the user had typed the URL themselves.
POST-based endpoints take a little more setup, but not much. A hidden form, auto-submitted the instant the page loads, does the job.
<form id="evil" action="https://bank.example.com/transfer" method="POST">
<input type="hidden" name="to" value="attacker" />
<input type="hidden" name="amount" value="500" />
</form>
<script>
document.getElementById("evil").submit();
</script>There's a small but real detail worth knowing here: display: none isn't always the safest way to hide it. Some modern browsers skip rendering fully hidden elements entirely, which can mean the form never gets attached to the page in a way JavaScript can reliably submit. opacity: 0 keeps the element technically rendered while making it invisible to the user, which is why it shows up as the more dependable choice in attacker code. Whether that stays true forever is genuinely unclear, browser rendering behavior around hidden elements keeps shifting.
Getting the Victim to the Page at All
None of this matters if the victim never lands on the attacker's page in the first place. That's a delivery problem, not the vulnerability itself, and it usually comes down to social engineering.
Phishing does a lot of the heavy lifting here. A link that looks legitimate at a glance is often just a long, official-looking path prefix hiding the real destination.
https://malicious-example.com/secure/login/paypal.com/confirm-accountTo a hurried reader, that reads like PayPal. It's a domain called malicious-example.com, and everything after the first slash is just a path, not a destination. Browsers have gotten better about surfacing the actual domain in the address bar, which helps, but a convincing enough link plus a distracted enough user is still all it takes to get someone onto the page that does the real damage.
What This Sets Up
Three ingredients, and the third one is where the fix hides. Before getting to that fix, it's worth seeing this attack actually work end to end, on a real endpoint, with a real session cookie doing exactly what it's supposed to do.
The Essentials
- Every CSRF attack needs three things at once: a worthwhile action, an auto-attached cookie, and no unpredictable parameter. Remove any one and the attack fails.
- The third ingredient, predictability, is where the eventual defense lives.
- CSRF needs no JavaScript injection and no code running on the target site at all. The attacker's page just has to get visited.
GET-based state changes can be triggered by something as plain as an<img>tag.POST-based ones need a hidden, auto-submitted form.opacity: 0is the more reliable hiding technique, since some browsers skip rendering fully hidden elements entirely.- Phishing and lookalike URLs are the delivery mechanism, not the vulnerability. A long path prefix can hide the real destination domain from a hurried reader.
Next up: proving all three ingredients are enough on their own, with a real transfer endpoint and nothing stolen except a click.
Further Reading and Watching
- Cross-Site Request Forgery (CSRF) explained: Detectify's breakdown of what makes a request forgeable and what the attacker actually needs.
- CSRF Attack Basics: PortSwigger's Web Security Academy reference on the exact conditions a CSRF attack requires.
Keep reading