CS 161, Lesson 39, in 50 slides, on cross-site request forgery. It shows how an attacker rides the victim's automatically attached session cookie to forge state-changing requests, in section 21.1, covers CSRF based on GET and on POST, and explains why the same-origin policy does not stop it. It then gives the three defenses - CSRF tokens, Referer validation, and the SameSite cookie attribute - in sections 21.2 to 21.4. It is anchored to textbook sections 21.1 to 21.4.
Subject: Computer Security · 83 slides · applied lesson
Open the interactive version of this deck · Homework for this lesson
Title
CS 161 · Lesson 39 of 45
How an attacker forges requests that ride your auto-attached session cookie — and why the same-origin policy doesn't stop it
Objectives
<img> or link) and POST-based CSRF (an auto-submitting form) against a toy target.Warm-up
Discussion prompt
Before we open L39 · Cross-Site Request Forgery (CSRF): without looking back, what was the main idea of L38 · Cookies & Session Management, and what could you do by the end of it that you could not do before?
Hint: One sentence for the idea, one for the skill. If the second one is blank, that is the part to revisit.
Answer:
CS 161, Lesson 38, in 53 slides. It explains why the statelessness of HTTP forces cookies, in section 20, then covers the cookie attributes in section 20.1, the send rule of domain-suffix plus path-prefix in section 20.2, and the set rule of suffix-of-server in section 20.3. It closes with session tokens and the gap between cookie policy and the same-origin policy that lets different origins share a cookie, in sections 20.4 and 20.5. It is anchored to textbook sections 20.1 to 20.5.
Concept
In L38 you saw that cookies keep you logged in: the browser attaches your session cookie to every request to a site. That convenience is exploitable — and CSRF is the attack that exploits it.
Matching
Match the pairs
From Why a whole lesson on CSRF — match each one to what it actually does. The descriptions have been shuffled.
Why: The attack, Why SOP fails, The defenses are easy to tell apart while they are sitting next to their descriptions and much harder afterwards, which is what this checks.
Section
Part 1 · §21.1 forging a request that rides the cookie
Concept
Concrete scenario: you're logged into bank.com in one tab. In another tab you open an attacker's page. That page silently makes your browser fire a request at bank.com — and bank.com treats it as if you meant it.
Cross-Site Request Forgery (CSRF) — An attack where the attacker forces the victim's browser to make an UNINTENDED request to a site the victim is logged into. The browser automatically attaches the session-token cookie, so the server accepts the request as if the victim made it intentionally.
Concept
Here's the crux. The browser attaches your bank.com session cookie to any request sent to bank.com — it does not care WHICH page triggered the request. So a request triggered by the attacker's page still carries your cookie.
The attacker never sees or steals the cookie. They can't read it. They just trigger a request that the browser then decorates with your cookie automatically. The damage is done server-side, by a request the victim never intended.
Intuition
Imagine you've pre-signed a stack of blank checks and left them with a trusted teller who fills in the details whenever a request 'from you' arrives. The signature is your session cookie; the teller is the browser.
The attacker can't COPY your signature — they never touch the checks. But they can mail the teller a note saying 'pay Mallory $100,' and the teller dutifully grabs a pre-signed check and processes it. Your signature was never stolen; it was just used.
Ask yourself: did the attacker need to know your signature? (No — they only needed to trigger a request the browser would sign for them.)
Counterexample
Discussion prompt
Ask yourself: did the attacker need to know your signature? (No — they only needed to trigger a request the browser would sign for them.)
That is stated as though it always holds. Do one of two things: produce a case where it fails, or say precisely what rules such a case out. "It just does" is not on the menu.
Hint: Hunt at the extremes first — zero, one, negative, empty, equal. If every extreme survives, the reason they survive is the proof.
Ranking
Put in order
Put the moves of §21.1 Walk the CSRF flow end to end into the order they have to happen.
Why: These are the moves of the worked example in the order it makes them, and each one is set up by the one before it. From L38: this cookie is how bank.com recognizes the victim on later requests.
Worked example
The victim logs into bank.com; the browser stores a session cookie for bank.com
Why: From L38: this cookie is how bank.com recognizes the victim on later requests.
The victim, still logged in, visits the attacker's page evil.com
Why: The attacker's page contains HTML that will trigger a request to bank.com.
evil.com's content makes the victim's browser send a request to bank.com
Why: An <img>, a link, or an auto-submitting form — anything that issues an HTTP request to bank.com.
| step | who acts | what carries the cookie? |
|---|---|---|
| login | victim → bank.com | cookie set for bank.com |
| visit attacker | victim → evil.com | no bank.com cookie sent |
| forged request | browser → bank.com | YES — cookie auto-attached |
| server acts | bank.com | accepts it as the victim's intent |
Verify: did the attacker ever read the cookie? No — the browser attached it on its own
Why: §21.1: the request goes to bank.com, so the browser attaches bank.com's cookie regardless of which page triggered it. The attacker only supplied the trigger.
Comparison
Comparison matrix
From §21.1 Walk the CSRF flow end to end: refill the who acts column from what you know. The rest of the table is as it appeared.
| step | who acts | what carries the cookie? |
|---|---|---|
| login | victim → bank.com | cookie set for bank.com |
| visit attacker | victim → evil.com | no bank.com cookie sent |
| forged request | browser → bank.com | YES — cookie auto-attached |
| server acts | bank.com | accepts it as the victim's intent |
Concept
The 'cross-site' in CSRF names the trick exactly: the request originates from a different site (evil.com) than the one it targets and is authenticated against (bank.com). The victim's own browser is the bridge between the two.
From bank.com's side this is invisible — it just sees a well-formed, cookie-authenticated request. The cross-site origin of that request is precisely the fact bank.com cannot tell from the cookie alone, which is why a separate signal (a token, a Referer, a SameSite flag) is needed.
Anomaly
Predict first
A student writes this, and it looks reasonable:
A student: 'For CSRF to work, the attacker has to somehow grab the victim's session token or read their cookie first.'
It is wrong. Say what breaks — and say it before you turn the page.
Correct: The attacker NEVER sees or steals the cookie — they can't read it across origins.
A student: what does the attacker actually need to do?
Why: The attacker NEVER sees or steals the cookie — they can't read it across origins. The browser attaches it automatically to any request bound for bank.com. The attacker only needs to TRIGGER the request.
Trap
A student: 'For CSRF to work, the attacker has to somehow grab the victim's session token or read their cookie first.'
Assume the attacker must obtain the cookie/session token
Why: Wrong. The attacker NEVER sees or steals the cookie — they can't read it across origins. The browser attaches it automatically to any request bound for bank.com. The attacker only needs to TRIGGER the request.
A student: what does the attacker actually need to do?
Just trigger a request to the target; the browser supplies the cookie
Why: §21.1: CSRF rides the auto-attached cookie. The attacker triggers a request to bank.com; the browser decorates it with the victim's cookie. No theft of the token is needed — that's what makes CSRF so easy.
Section
Part 2 · §21.1 links and auto-loading images
Concept
Recall from L36: a GET should be side-effect-free, but the protocol doesn't enforce it. If a site wires a state-changing action to a GET endpoint, then merely loading that URL performs the action.
http://example.com/logout
-> just loading this URL logs the victim out
https://bank.com/transfer?amount=100&recipient=mallory
-> just loading this URL transfers $100 to malloryAnalogy
Discussion prompt
Explain §21.1 If a state change is a GET, a link triggers it by analogy to something with no Computer Security in it at all — a queue, a recipe, a map, a bank balance, whatever fits. Then say where your analogy breaks.
Hint: An analogy that never breaks is not an analogy, it is the same idea wearing a hat. Find the seam — that is the part that is actually new.
Answer:
Recall from L36: a GET should be side-effect-free, but the protocol doesn't enforce it. If a site wires a state-changing action to a GET endpoint, then merely loading that URL performs the action.
Concept
The attacker doesn't even need the victim to click. An <img> tag tells the browser to fetch its src with a GET as soon as the page (or email) loads. Point the src at the state-changing URL.
<img src="https://bank.com/transfer?amount=100&recipient=mallory">When the victim opens the email or page, the browser issues a GET to that URL, attaches the bank.com cookie, and the transfer goes through. The 'image' never renders — but the request already fired.
Explain it
Discussion prompt
Explain §21.1 The auto-loading <img> trick to a student a year behind you. No notation, no jargon they have not met — and it still has to be true.
Hint: If your explanation needs a symbol they have never seen, you are describing the notation rather than the idea.
Answer:
The attacker doesn't even need the victim to click. An <img> tag tells the browser to fetch its src with a GET as soon as the page (or email) loads. Point the src at the state-changing URL.
Intuition
The browser sees <img src=...> and thinks 'I'd better go fetch that so I can show it.' It fires the GET FIRST and only afterward discovers the response isn't a valid image. By then the request — cookie and all — has already reached the server.
Ask yourself: does it matter that the response isn't really an image? (No — CSRF lives in the REQUEST. The transfer happened the instant the GET arrived; whether the response is a JPEG is irrelevant.)
Step zero
Discussion prompt
§21.1 Trace the <img> CSRF — before any calculation: what is the plan? Name the moves in order, in plain English, without doing the arithmetic.
Hint: It starts with: Attacker emails the victim an HTML email containing an <img> tag
Answer:
Worked example
Attacker emails the victim an HTML email containing an <img> tag
Why: The src points at bank.com/transfer?amount=100&recipient=mallory — a state-changing GET.
<!-- in the body of an email -->
<img src="https://bank.com/transfer?amount=100&recipient=mallory">The victim (logged into bank.com) opens the email; the browser fetches the 'image'
Why: Loading an <img> issues a GET to its src automatically — no click required.
| event | request sent? | cookie attached? | effect |
|---|---|---|---|
| email opens | GET to bank.com/transfer | yes (bank.com cookie) | server queues transfer |
| server checks auth | — | cookie = valid session | looks like the victim |
| server executes | — | — | $100 → mallory |
| browser 'renders' | — | — | broken image icon |
Verify: the broken-image icon is the only sign — and the money is already gone
Why: §21.1: the GET fired and carried the cookie before the browser ever inspected the response. The damage is in the request, so a non-image response changes nothing.
Discrimination
Sort into buckets
Sort these by request sent?, from memory, without looking back at §21.1 Trace the <img> CSRF. Telling them apart on the spot is the skill; the table is only where the answer happens to be written down.
Concept
An <img> fires automatically, but a plain hyperlink works the same way if the victim clicks it. Disguise the state-changing URL as something tempting and the click triggers the authenticated GET.
<a href="https://bank.com/transfer?amount=100&recipient=mallory">
Click here to claim your free prize!
</a>The <img> version is strictly stealthier — no click required — but both rely on the identical fact: loading or navigating to a bank.com URL sends a GET with the bank.com cookie attached.
Concept
Because state-changing GET endpoints make CSRF this trivial, they're considered bad practice today — most sensitive actions are POST. So the pure <img>-CSRF form is less common now.
But moving to POST is not a fix — it just raises the bar slightly. As the next part shows, CSRF works fine over POST too.
Anomaly
Predict first
A student writes this, and it looks reasonable:
A student: 'An <img> only loads images, so pointing its src at a transfer URL just produces a broken image — nothing happens.'
It is wrong. Say what breaks — and say it before you turn the page.
Correct: The browser issues the GET to the src REGARDLESS of what comes back.
A student: what does <img src=URL> actually do?
Why: The browser issues the GET to the src REGARDLESS of what comes back. It can't know it isn't an image until after the request is sent. The response just isn't a valid image — but the GET (with the cookie) already happened.
Trap
A student: 'An <img> only loads images, so pointing its src at a transfer URL just produces a broken image — nothing happens.'
Assume <img> won't fire a request unless the URL is a real image
Why: Wrong. The browser issues the GET to the src REGARDLESS of what comes back. It can't know it isn't an image until after the request is sent. The response just isn't a valid image — but the GET (with the cookie) already happened.
A student: what does <img src=URL> actually do?
It issues a GET to the URL with the site's cookie, then tries to draw the result
Why: §21.1: the request comes first, rendering second. So an <img> can trigger any GET endpoint, image or not. That's exactly what GET-based CSRF abuses.
Section
Part 3 · §21.1 auto-submitting forms
Concept
Suppose bank.com/transfer now requires a POST. CSRF still works: the attacker's page builds a hidden HTML form aimed at bank.com and uses JavaScript to auto-submit it the moment the victim arrives.
Submitting a form sends an HTTP request — here a POST to bank.com — and, like any request to bank.com, the browser attaches the session cookie. The server executes the transfer as the victim.
Concept
The attacker page hides a form whose action is the bank's POST endpoint and whose inputs carry the attacker's chosen values. A one-line script submits it automatically.
<form name=evilform action=https://bank.com/transfer method=POST>
<input name=amount value=100>
<input name=recipient value=mallory>
</form>
<script>document.evilform.submit();</script>No button, no click. document.evilform.submit() runs on page load, the browser POSTs to bank.com WITH the cookie, and bank.com transfers $100 to mallory.
Intuition
We tend to picture a form as something a human fills out and clicks 'Submit' on. But to the browser a form submit is just an HTTP request — and JavaScript can fire it without any human at all.
Ask yourself: from bank.com's point of view, does a POST from an auto-submitted evil.com form look any different from a POST from its own page? (No — same method, same fields, same cookie. That's the whole problem.)
Sorting
Sort into buckets
These are the pieces of L39 · Cross-Site Request Forgery (CSRF), out of order. Put each one back under the part of the lesson it belongs to.
Step zero
Discussion prompt
§21.1 Walk the auto-submit POST CSRF — before any calculation: what is the plan? Name the moves in order, in plain English, without doing the arithmetic.
Hint: It starts with: The victim is logged into bank.com and visits the attacker's page
Answer:
Worked example
The victim is logged into bank.com and visits the attacker's page
Why: The attacker page holds the hidden form + auto-submit script shown above.
On load, document.evilform.submit() runs
Why: The script fires immediately — the victim does nothing and clicks nothing.
The browser sends a POST to https://bank.com/transfer with amount=100, recipient=mallory
Why: Submitting the form issues the request; the browser attaches bank.com's session cookie because the request targets bank.com.
| region of the POST | content | where it came from |
|---|---|---|
| first line | POST /transfer HTTP/1.1 | the form's method + action |
| Host | bank.com | the action's domain |
| Cookie | session=<victim's> | browser auto-attached it |
| body | amount=100&recipient=mallory | the form's hidden inputs |
Verify: bank.com sees a valid session cookie + a transfer request, and executes it
Why: §21.1: nothing distinguishes this forged POST from a real one. Switching the endpoint to POST did not stop the attack — it only required a form instead of an <img>.
Trade off
Comparison matrix
From §21.1 Walk the auto-submit POST CSRF: every row here is a choice with a cost. Fill the where it came from column, then say which row you would actually pick and what you give up for it.
| region of the POST | content | where it came from |
|---|---|---|
| first line | POST /transfer HTTP/1.1 | the form's method + action |
| Host | bank.com | the action's domain |
| Cookie | session=<victim's> | browser auto-attached it |
| body | amount=100&recipient=mallory | the form's hidden inputs |
Concept
The attacker's form fields can be type=hidden, so nothing renders for the victim to notice. Combined with auto-submit, the whole forgery happens in the instant the attacker page loads — the victim may never even see it flash by.
The attacker also pre-fills every value (amount, recipient), so they fully control the forged action. The victim contributes only one thing: a valid logged-in session the browser silently attaches.
Anomaly
Predict first
A student writes this, and it looks reasonable:
A student: 'If I make the transfer endpoint require a POST instead of a GET, attackers can't forge it anymore.'
It is wrong. Say what breaks — and say it before you turn the page.
Correct: An attacker page can host a hidden form and auto-submit it with JavaScript, sending a POST to bank.com with the cookie attached.
A student: what does requiring POST actually buy you?
Why: An attacker page can host a hidden form and auto-submit it with JavaScript, sending a POST to bank.com with the cookie attached. POST raises the bar slightly over a bare <img>, but it does NOT stop CSRF.
Trap
A student: 'If I make the transfer endpoint require a POST instead of a GET, attackers can't forge it anymore.'
Treat POST-only as a CSRF defense
Why: Wrong. An attacker page can host a hidden form and auto-submit it with JavaScript, sending a POST to bank.com with the cookie attached. POST raises the bar slightly over a bare <img>, but it does NOT stop CSRF.
A student: what does requiring POST actually buy you?
Almost nothing — auto-submitted forms send POSTs too
Why: §21.1: CSRF works over POST via auto-submitting forms. The real fix is an unguessable per-session token, not the choice of HTTP method.
Section
Part 4 · §21.1 the key insight
Concept
The Same-Origin Policy (L37) blocks the attacker's page from reading bank.com's RESPONSE. So why doesn't it stop CSRF? Because CSRF doesn't need the response at all.
The attacker's page is still allowed to SEND a request to bank.com — that's what <img>, links, and form submits do every day. SOP only stops the attacker from READING what comes back. The request goes out, cookie and all, before SOP has anything to say.
Concept
CSRF's whole effect — the transfer, the logout, the state change — happens server-side, when the request arrives. The attacker doesn't care what bank.com replies; they already won the moment the request executed.
So a policy that guards the response (SOP) is the wrong tool against an attack whose payload is the request. SOP blocking the read is irrelevant when the harm is already done by the send.
Intuition
Picture SOP as a lock on bank.com's incoming-mail box — it stops the attacker from reading the replies bank.com sends back. Fine. But the attacker isn't trying to read replies.
The attacker just drops a letter into the mail slot — a request that triggers an action. SOP never locked the slot; it only locked the reply box. CSRF posts a letter and walks away, never needing to read the answer.
Ask yourself: which direction does SOP protect? (The response coming back. CSRF attacks the request going out — the unguarded direction.)
Ranking
Put in order
Put the moves of §21.1 Reason out why SOP can't catch the forged POST into the order they have to happen.
Why: These are the moves of the worked example in the order it makes them, and each one is set up by the one before it. This is a cross-origin request: source origin evil.com, target origin bank.com.
Worked example
evil.com auto-submits a form POSTing to bank.com/transfer
Why: This is a cross-origin request: source origin evil.com, target origin bank.com.
Ask what SOP actually governs at each step
Why: SOP restricts cross-origin READS of responses; it does not block the SENDING of the request itself.
| step | happens? | does SOP intervene? |
|---|---|---|
| browser sends POST to bank.com | yes | no — sending is allowed |
| browser attaches bank.com cookie | yes | no — cookie policy, not SOP |
| bank.com executes the transfer | yes | no — it's server-side |
| evil.com's JS reads the response | blocked | YES — but attacker doesn't care |
Verify: the only step SOP blocks is the one the attacker never needed
Why: §21.1: the harm (the transfer) completes in the first three rows, all unguarded by SOP. SOP only kicks in at the read, which is irrelevant to a fire-and-forget attack. Hence a dedicated CSRF defense is required.
Comparison
Comparison matrix
From §21.1 Reason out why SOP can't catch the forged POST: refill the does SOP intervene? column from what you know. The rest of the table is as it appeared.
| step | happens? | does SOP intervene? |
|---|---|---|
| browser sends POST to bank.com | yes | no — sending is allowed |
| browser attaches bank.com cookie | yes | no — cookie policy, not SOP |
| bank.com executes the transfer | yes | no — it's server-side |
| evil.com's JS reads the response | blocked | YES — but attacker doesn't care |
Anomaly
Predict first
A student writes this, and it looks reasonable:
A student: 'The Same-Origin Policy stops evil.com from talking to bank.com, so a CSRF request would be blocked.'
It is wrong. Say what breaks — and say it before you turn the page.
Correct: SOP blocks the attacker from READING bank.com's response — it does NOT block the request from being SENT.
A student: what exactly does SOP restrict?
Why: SOP blocks the attacker from READING bank.com's response — it does NOT block the request from being SENT. Cross-site requests fire constantly (every <img>, link, and form). The cookie rides along and the server acts on it.
Trap
A student: 'The Same-Origin Policy stops evil.com from talking to bank.com, so a CSRF request would be blocked.'
Assume SOP blocks the cross-site REQUEST from being sent
Why: Wrong. SOP blocks the attacker from READING bank.com's response — it does NOT block the request from being SENT. Cross-site requests fire constantly (every <img>, link, and form). The cookie rides along and the server acts on it.
A student: what exactly does SOP restrict?
SOP restricts reading the response, not sending the request
Why: §21.1: the forged request is sent and executed with the cookie attached; SOP only stops the attacker from seeing the reply. Since CSRF's damage is the request itself, SOP doesn't help — that's why a separate defense (a CSRF token) is needed.
Section
Part 5 · §21.2–21.4 in order of strength
Concept
The primary defense: the server embeds a random, unpredictable token as a (usually hidden) extra field in each legitimately-served form, and stores a mapping from CSRF token → session token. On submit, it checks that the token matches the session.
CSRF token — A random, unpredictable value the server places in each form it serves and maps to the user's session. A request is accepted only if it carries a token bound to that session — which a forged cross-site request can't supply.
Definition probe
Sort into buckets
Every line below is part of the definition of Cross-Site Request Forgery (CSRF) or of CSRF token — one or the other, never both. Put each where it belongs.
Concept
The legit form bank.com serves includes a hidden input holding the token. When the victim submits the real form, the token rides along and the server checks it against the session.
<!-- served by bank.com to a logged-in victim -->
<form action="https://bank.com/transfer" method="POST">
<input type="hidden" name="csrf_token" value="7Fx9...random...Qz2">
<input name="amount">
<input name="recipient">
</form>The attacker's forged form has no valid csrf_token. They can't guess it (it's random) and can't read it out of the victim's real page (SOP blocks the cross-origin read). So the server rejects the forged request.
Intuition
The token works because the attacker faces two walls at once. First, it's random, so they can't guess it. Second, the victim never legitimately loaded the form from the attacker's context, so there's no token bound to the victim's session for the attacker to copy.
And even if the token sits in the victim's real bank.com page, SOP stops the attacker's cross-origin JavaScript from reading it out of that page. The same SOP that fails to stop the request succeeds at hiding the token.
Note the cost: this needs server state — a token↔session mapping the server maintains and checks.
Step zero
Discussion prompt
§21.2 Why the forged request fails the token check — before any calculation: what is the plan? Name the moves in order, in plain English, without doing the arithmetic.
Hint: It starts with: bank.com serves the victim a form with a fresh hidden csrf_token…
Answer:
Worked example
bank.com serves the victim a form with a fresh hidden csrf_token bound to the victim's session
Why: The server records the mapping csrf_token -> session in its state.
evil.com auto-submits a forged POST to bank.com/transfer
Why: The cookie still rides along automatically — but the forged form has no valid token.
| request | cookie? | valid csrf_token? | server's decision |
|---|---|---|---|
| victim's real form | yes | yes (bound to session) | ACCEPT |
| forged form (no token) | yes | no — missing | REJECT |
| forged form (guessed token) | yes | no — won't match | REJECT |
| forged form (read token?) | yes | can't — SOP blocks read | REJECT |
Verify: the cookie alone is no longer enough — the request also needs a session-bound token the attacker can't obtain
Why: §21.2: the token separates 'a request that merely carries the cookie' from 'a request the user actually initiated on a real form.' That's why it's the primary defense.
Discrimination
Sort into buckets
Sort these by server's decision, from memory, without looking back at §21.2 Why the forged request fails the token check. Telling them apart on the spot is the skill; the table is only where the answer happens to be written down.
Concept
The browser's Referer header tells the server which URL the request came from — bank.com for a legit form, evil.com for the attack. So the server can reject requests whose Referer it doesn't trust.
(Fun fact: Referer is a decades-old misspelling of 'referrer,' baked into the HTTP spec and now permanent.)
Concept
The problem: the Referer is often blank or stripped — for privacy reasons, by proxies, or by browser settings. So the server faces a dilemma with no good answer.
Neither choice is clean, so Referer validation is only defense-in-depth — a useful extra layer, never a standalone fix.
Explain it
Discussion prompt
Explain §21.3 Why Referer is only defense-in-depth to a student a year behind you. No notation, no jargon they have not met — and it still has to be true.
Hint: If your explanation needs a symbol they have never seen, you are describing the notation rather than the idea.
Answer:
The problem: the Referer is often blank or stripped — for privacy reasons, by proxies, or by browser settings. So the server faces a dilemma with no good answer.
Concept
A cookie can be flagged SameSite: the browser then sends it only when the cookie's domain matches the request's origin domain. So a request from evil.com to bank.com simply won't carry bank.com's cookie — defusing CSRF at the source.
SameSite cookie attribute — A flag on a cookie telling the browser to send it only when the request's origin domain matches the cookie's domain. A cross-site request (evil.com → bank.com) then arrives without the cookie, so the server doesn't see a logged-in session.
Historically it wasn't implemented or behaved consistently across all browsers, so it's treated as defense-in-depth rather than a sole defense.
Analogy
Discussion prompt
Explain §21.4 The SameSite cookie attribute (defense-in-depth) by analogy to something with no Computer Security in it at all — a queue, a recipe, a map, a bank balance, whatever fits. Then say where your analogy breaks.
Hint: An analogy that never breaks is not an analogy, it is the same idea wearing a hat. Find the seam — that is the part that is actually new.
Answer:
Historically it wasn't implemented or behaved consistently across all browsers, so it's treated as defense-in-depth rather than a sole defense.
Intuition
The other two defenses let the cookie arrive and then scrutinize the request. SameSite is more direct: it tells the browser not to send the cookie in the first place when the request comes from a different site.
Think of the cookie as refusing to ride along unless the request's 'return address' (its origin site) matches the cookie's own home. A request stamped 'from evil.com' headed to bank.com simply travels without the bank.com cookie — so bank.com sees no logged-in session and won't act.
Ask yourself: why is this still only defense-in-depth? (Because historically not every browser implemented it the same way — you can't rely on it being enforced everywhere, so you don't drop the token.)
Concept
Three defenses, in order of strength. Read each as 'how it works' and, crucially, 'what its limitation is.'
| defense | how it works | limitation |
|---|---|---|
| CSRF token (§21.2) | random session-bound token in each form; checked on submit; attacker can't read or guess it (SOP blocks the read) | needs server state (token↔session mapping) |
| Referer validation (§21.3) | reject requests whose Referer isn't the site itself | Referer often blank/stripped → must break users or accept a hole |
| SameSite cookie (§21.4) | browser won't send the cookie on cross-site requests at all | historically inconsistent browser support |
Counterexample
Discussion prompt
Three defenses, in order of strength. Read each as 'how it works' and, crucially, 'what its limitation is.'
That is stated as though it always holds. Do one of two things: produce a case where it fails, or say precisely what rules such a case out. "It just does" is not on the menu.
Hint: Hunt at the extremes first — zero, one, negative, empty, equal. If every extreme survives, the reason they survive is the proof.
Ranking
Put in order
Put the moves of §21.2–21.4 Run one forged request past all three defenses into the order they have to happen.
Why: These are the moves of the worked example in the order it makes them, and each one is set up by the one before it. Same forged request as before — now we see what each defense does to it.
Worked example
evil.com auto-submits a POST to bank.com/transfer; the browser attaches the cookie
Why: Same forged request as before — now we see what each defense does to it.
| defense in place | what the forged request lacks | outcome |
|---|---|---|
| CSRF token | no valid session-bound token (can't read/guess it) | REJECTED — reliably |
| Referer validation | Referer says evil.com (or is blank) | rejected if Referer present; HOLE if blank |
| SameSite cookie | cookie not sent on the cross-site request | rejected where SameSite is enforced |
Note which defense fails open and why
Why: Referer validation fails open on blank Referers, and SameSite fails where a browser doesn't enforce it — so both are defense-in-depth around the token.
Verify: only the CSRF token rejects the request without depending on the browser or the Referer being well-behaved
Why: §21.2–21.4: the token is self-contained server-side state the attacker can't satisfy, which is why it's the primary defense and the other two are layered on top.
Trade off
Comparison matrix
From §21.2–21.4 Run one forged request past all three defenses: every row here is a choice with a cost. Fill the outcome column, then say which row you would actually pick and what you give up for it.
| defense in place | what the forged request lacks | outcome |
|---|---|---|
| CSRF token | no valid session-bound token (can't read/guess it) | REJECTED — reliably |
| Referer validation | Referer says evil.com (or is blank) | rejected if Referer present; HOLE if blank |
| SameSite cookie | cookie not sent on the cross-site request | rejected where SameSite is enforced |
Concept
Students conflate CSRF and XSS, but they differ on one axis: does the attacker get to run code inside the target origin? CSRF does NOT — it only triggers a request from the outside and never reads the response.
| CSRF (L39) | XSS (L40) | |
|---|---|---|
| attacker code runs in target origin? | no | YES |
| reads the response? | no — fire and forget | yes — full DOM access |
| rides the auto-attached cookie? | yes | yes (and can read it) |
| severity | forge specific requests | far worse — owns the page |
CSRF rides the cookie from outside; XSS breaks INTO the origin and runs script there. That's why XSS (next lesson) is strictly more powerful.
Comparison
Comparison matrix
From §21.1 CSRF vs XSS — a sharp line: refill the XSS (L40) column from what you know. The rest of the table is as it appeared.
| CSRF (L39) | XSS (L40) | |
|---|---|---|
| attacker code runs in target origin? | no | YES |
| reads the response? | no — fire and forget | yes — full DOM access |
| rides the auto-attached cookie? | yes | yes (and can read it) |
| severity | forge specific requests | far worse — owns the page |
Anomaly
Predict first
A student writes this, and it looks reasonable:
A student: 'I'll just check the Referer header and reject anything not from bank.com — that completely stops CSRF.'
It is wrong. Say what breaks — and say it before you turn the page.
Correct: The Referer is often blank or stripped (privacy, proxies, browser settings).
A student: where does Referer validation fit?
Why: The Referer is often blank or stripped (privacy, proxies, browser settings). If you reject blank Referers you break real users; if you accept them you leave a CSRF hole. Either way it can't stand alone.
Trap
A student: 'I'll just check the Referer header and reject anything not from bank.com — that completely stops CSRF.'
Rely on Referer validation as the sole defense
Why: Wrong. The Referer is often blank or stripped (privacy, proxies, browser settings). If you reject blank Referers you break real users; if you accept them you leave a CSRF hole. Either way it can't stand alone.
A student: where does Referer validation fit?
Use it as defense-in-depth behind a CSRF token
Why: §21.3: Referer validation is a useful extra layer but unreliable because of stripped headers. The primary defense is the CSRF token; Referer and SameSite are defense-in-depth on top of it.
Two truths and a lie
Sort into buckets
Some of these hold up and some are the exact mistakes this lesson is built to prevent. Sort them.
action is the bank's POST endpoint and whose inputs carry the attacker's chosen values. A one-line script submits it automatically.; The Same-Origin Policy (L37) blocks the attacker's page from reading bank.com's RESPONSE. So why doesn't it stop CSRF? Because CSRF doesn't need the response at all.<img> only loads images, so pointing its src at a transfer URL just produces a broken image — nothing happens.'Constraint
Discussion prompt
Run The CSRF playbook with this step confiscated:
Why SOP doesn't save you: SOP blocks READING the response, not SENDING the request; the damage is server-side in the request, so SOP is irrelevant.
Is it still possible? If it is, say what takes its place and what it costs you. If it is not, say exactly what that step was providing that nothing else does.
Hint: A step you can drop for free was never load-bearing. If you cannot drop it, name the thing that goes wrong the moment it is gone.
Answer:
<img src>/link (if state-changing GETs exist), or a POST…Pattern
<img src>/link (if state-changing GETs exist), or a POST via a hidden auto-submitting <form> + document.form.submit().Edge cases
Discussion prompt
The CSRF playbook works on the cases you have just seen. Push it to the edge: what is the most degenerate input it still handles — empty, zero, one item, everything equal — and what is the first case where it stops being true? Name the case, not just "it breaks".
Hint: Try the smallest legal input, then the largest, then the one where two things collide. Methods are specified at their edges; the middle takes care of itself.
Answer:
<img src>/link (if state-changing GETs exist), or a POST…Elimination
Eliminate the wrong options
Which statement about this CSRF attack is TRUE?
3 of these 4 are wrong. Strike them one at a time, and say what rules each one out before you strike the next. The survivor is the answer.
Survives elimination: D
Why: §21.1–21.2: because the forged POST targets bank.com, the browser automatically attaches the victim's session cookie, and the server would treat it as the victim's intent. The real defense is a CSRF token: a random, session-bound value the server places in each legitimate form and verifies on submit. The attacker can neither guess it nor read it out of the victim's real page (SOP blocks the cross-origin read), so the forged request is rejected. The other options each rest on a common misconception about CSRF.
Check
A victim is logged into bank.com and visits evil.com, which auto-submits a hidden form POSTing to bank.com/transfer. Think through cookie attachment, the role of the SOP, and how a CSRF token would change the outcome before choosing.
Check your understanding
Which statement about this CSRF attack is TRUE?
Answer: D
Why: §21.1–21.2: because the forged POST targets bank.com, the browser automatically attaches the victim's session cookie, and the server would treat it as the victim's intent. The real defense is a CSRF token: a random, session-bound value the server places in each legitimate form and verifies on submit. The attacker can neither guess it nor read it out of the victim's real page (SOP blocks the cross-origin read), so the forged request is rejected. The other options each rest on a common misconception about CSRF.
Concept
<img> only loads images — no: it issues the GET regardless; the response just isn't a valid image.Concept
<img>, forms), together let any page fire an authenticated request on your behalf.Concept
Connect it up
Draw it
One page, no notation unless you need it: draw how these connect — The CSRF Attack · GET-Based CSRF · POST-Based CSRF · Why the SOP Doesn't Stop CSRF · Defenses — Token, Referer, SameSite. Put an arrow wherever one of them is what makes another possible, and label the arrow with why.
Recap
You can now define CSRF as a forged, unintended request riding the browser's auto-attached session cookie; build it over GET (an <img>) and POST (an auto-submitting form); explain why the Same-Origin Policy doesn't stop it (SOP guards reads, not sends); and rank the three defenses — CSRF token (primary), Referer validation and SameSite (defense-in-depth) — while distinguishing CSRF from XSS.
| Idea | § | The one-line version |
|---|---|---|
| The attack | 21.1 | forced unintended request; browser auto-attaches the cookie |
| No theft | 21.1 | attacker never reads the cookie — only triggers the request |
| GET CSRF | 21.1 | <img src=transfer-url> fires a GET with the cookie |
| POST CSRF | 21.1 | hidden auto-submitting form POSTs with the cookie |
| Why SOP fails | 21.1 | SOP blocks reading the RESPONSE, not sending the request |
| CSRF token | 21.2 | random session-bound token; primary defense; needs server state |
| Referer / SameSite | 21.3–21.4 | defense-in-depth: blank Referers; inconsistent SameSite |
| vs XSS | — | CSRF rides the cookie; XSS (L40) runs script in the origin |
Want this taught 1-on-1? Alexander tutors Computer Security — $55/session, free consultation.