Imagine you're logged into your bank in one browser tab. In another tab, you open a forum post someone linked you. That forum page silently contains an image tag pointing not at a picture, but at your-bank.example/transfer?to=attacker&amount=5000. Your browser dutifully tries to load the "image" — and because it's a request to your bank, it attaches your bank session cookie automatically. From the bank's perspective, the request arrives fully authenticated. It looks exactly like you clicking the transfer button.
That, in one paragraph, is Cross-Site Request Forgery (CSRF, sometimes pronounced "sea-surf"). It's an attack against the trust a site places in your browser, not against the site's password check. And it exploits a feature, not a bug: browsers send your cookies with every request to a domain, regardless of which site initiated that request.
Why Cookies Make This Possible
The root cause is something cryptographers call ambient authority. When you authenticate to a site, the server hands your browser a session cookie. From then on, the browser attaches that cookie to every request bound for that domain — the click you made, the image that loads, the form a third party submitted on your behalf. The cookie is ambient: it's just there, applied automatically, with no check on who actually triggered the request.
That's enormously convenient. It's why you don't re-enter your password on every page. But it means the server, on its own, cannot tell the difference between "the user clicked this button on our page" and "some other site told the user's browser to send this." Both arrive with the same valid cookie. The session is real; the intent behind the request is forged.
A CSRF attacker cannot read your cookies, and cannot see the response your bank sends back (the same-origin policy blocks that). All they can do is cause a request to be sent. For state-changing actions — transfer money, change email, delete account — causing the request is the whole game.
What an Attack Actually Looks Like
The attacker's payload lives on a site you visit while logged into the target. The classic forms:
- Image and link tags for GET-based actions:
<img src="bank.example/logout">fires the moment the page renders, no click required. - Auto-submitting hidden forms for POST actions. A form with the target's fields, pointed at the target URL, submitted by a one-line script as the page loads. Because the browser still attaches the cookie, the POST is authenticated.
- Hidden iframes to hide the whole thing from view so you never notice the request went out.
Notice what's missing: any need to defeat encryption, guess a password, or break TLS. CSRF is a logic gap, not a cryptographic one. It also explains an old security rule that confuses newcomers: GET requests must never change state. A "delete" or "transfer" that works over GET can be triggered by something as innocent as an image tag. Side-effecting actions belong on POST, PUT, or DELETE — and even those need the defenses below.
The Three Defenses That Work
There's no single switch. Modern protection layers a few mechanisms, and a serious application uses more than one.
1 — Anti-CSRF tokens
The classic defense, often called the synchronizer token pattern. The server generates a random, unpredictable token tied to your session, embeds it in every form it sends you, and requires it back on every state-changing request. A cross-site attacker can cause a request, but they can't read the token (the same-origin policy stops them) and can't guess it. No valid token, request rejected. This is the same "you must possess a secret you couldn't have observed" logic that underpins a lot of authentication design.
2 — SameSite cookies
A newer, structural fix that addresses the ambient-authority problem directly. The SameSite cookie attribute tells the browser when to withhold a cookie on cross-site requests:
| SameSite value | Cookie sent on cross-site requests? |
|---|---|
| Strict | Never. Maximum protection, but you arrive logged-out when following a link from another site. |
| Lax | Only on top-level navigations using safe methods (GET). Blocks cross-site POSTs and background requests. The modern default in major browsers. |
| None | Always — the old behavior. Must be paired with the Secure flag, and reopens the CSRF door. |
Because browsers now default unlabeled cookies to Lax, a large class of CSRF attacks — cross-site POSTs and background image/iframe requests — fail automatically. This is real progress, but it is not a complete solution: Lax still permits top-level GET navigations, browser support varies at the edges, and you should never assume every visitor's browser enforces it.
3 — Origin and Referer checks
The server can inspect the Origin (and as a fallback Referer) header the browser attaches to requests, and reject any state-changing request that didn't originate from its own site. Browsers don't let JavaScript forge these headers, so the check is meaningful. It's typically used as defense-in-depth alongside tokens rather than on its own.
Use SameSite=Lax (or Strict) as your baseline, require anti-CSRF tokens on every state-changing endpoint, and verify the Origin header on top of that. Any one of these can have an edge case; together they close the gap.
Where CSRF Sits Among Web Attacks
It's worth distinguishing CSRF from its frequently-confused neighbors. XSS (cross-site scripting) runs attacker-controlled code inside the trusted page, which lets it read tokens and cookies — and an XSS hole defeats most CSRF defenses, which is why XSS is the more severe bug. Clickjacking tricks you into clicking a real button on a real page hidden under a decoy, rather than forging the request directly. CSRF is the narrowest of the three: it forges a single request and relies entirely on your existing session.
CSRF is the web's version of a forged signature. The bank knows the account is yours and the request is well-formed. What it can't see, without help, is that you never authorized it.
What This Means For You
As a user, you can't patch a site's CSRF holes, but you can shrink the window: log out of sensitive accounts when you're done rather than leaving them open in a background tab, and be wary of clicking links into a session you care about. Browser isolation features — container tabs, separate profiles — also help by keeping sensitive sessions away from your general browsing.
As a builder, treat CSRF as a baseline requirement, not an advanced concern. The defenses are well understood and cheap to apply. The expensive mistakes are the ones where a side-effecting action was quietly exposed over GET, or a cookie was set to SameSite=None for convenience and never revisited.
The deeper lesson generalizes beyond CSRF: authentication is not authorization, and a valid session is not the same as a deliberate action. Good systems are designed so that doing something meaningful requires proof you actually meant to do it — a principle that runs all the way down to how we think about session tokens and account safety at Haven.