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.
Subject: Computer Security · 83 slides · applied lesson
Open the interactive version of this deck · Homework for this lesson
Title
CS 161 · Lesson 38 of 45
Why stateless HTTP needs cookies · cookie attributes · the send & set rules · session tokens and the cookie-policy-vs-SOP gap
Objectives
Set-Cookie response and the follow-up request that carries the cookie back.Domain, Path, Secure, HttpOnly, expires — and say exactly what each controls.Domain is a domain-SUFFIX of the URL and Path is a PREFIX of the URL path) to decide which URLs get a cookie.Domain is a SUFFIX of its own URL, never a bare TLD).Warm-up
Discussion prompt
Before we open L38 · Cookies & Session Management: without looking back, what was the main idea of L37 · The Same-Origin Policy, 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 37, in 50 slides. It covers the browser's core isolation boundary: why the same-origin policy exists, in section 19; what an origin is, namely the scheme, host, and port, in section 19.1; and what the policy restricts against what it allows, which is the difference between reading and embedding, in section 19.2. That same section also gives the exam-critical rules for assigning an origin to scripts, images, and frames, and covers controlled cross-origin communication through postMessage. It is anchored to textbook sections 19.1 to 19.2.
Concept
Last lesson the web was stateless — each HTTP request stands alone. But staying logged in, remembering dark mode, keeping a shopping cart all need state across requests. Cookies are the patch, and they're the substrate the next two attacks (CSRF, XSS) abuse.
Matching
Match the pairs
From Why a whole lesson on cookies — match each one to what it actually does. The descriptions have been shuffled.
Why: Why do cookies exist?, Who can touch a cookie?, How do logins work? 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 · §20 state on a stateless protocol
Concept
Concrete scenario: you log in, then click to a new page. From HTTP's point of view, that second request is a total stranger — the protocol carries no memory of the first one.
Stateless protocol — Each HTTP request/response is independent: the server gets no built-in information about any previous request from the same client. Nothing in HTTP itself 'remembers' who you are between requests.
Counterexample
Discussion prompt
Concrete scenario: you log in, then click to a new page. From HTTP's point of view, that second request is a total stranger — the protocol carries no memory of the first one.
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.
Concept
Real sites need to remember things ACROSS requests: that you're logged in, that you picked dark mode, what's in your shopping cart. None of that survives if every request is a stranger.
So we need a way to carry a little state from one request to the next. The fix lives in the browser: cookies.
Analogy
Discussion prompt
Explain §20 But many features need state 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:
Real sites need to remember things ACROSS requests: that you're logged in, that you picked dark mode, what's in your shopping cart. None of that survives if every request is a stranger.
Concept
A cookie is a small piece of state the server asks the browser to store. The server's response includes a Set-Cookie header; on future requests the browser automatically re-attaches the relevant cookies.
Cookie — A piece of state stored in the browser at the server's request. The server sends Set-Cookie to store it; the browser then automatically includes it (via the Cookie header) on future requests to which it applies.
Definition probe
Sort into buckets
Every line below is part of the definition of Stateless protocol or of Cookie — one or the other, never both. Put each where it belongs.
Set-Cookie to store it; the browser then automatically includes it (via the Cookie header) on future requests to which it applies.Set-Cookie to store it; the browser then automatically includes it (via the Cookie header) on future requests to which it applies.Concept
Scenario: you visit example.com. The server's response carries a Set-Cookie header telling the browser to store a cookie — here, a name=value pair recording your theme choice.
HTTP/1.1 200 OK
Content-Type: text/html
Set-Cookie: darkmode=true
<html> … </html>darkmode=true is the cookie. The browser reads the Set-Cookie header and stores the cookie locally, associated with this site.
Explain it
Discussion prompt
Explain §20 The Set-Cookie response 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:
Scenario: you visit example.com. The server's response carries a Set-Cookie header telling the browser to store a cookie — here, a name=value pair recording your theme choice.
Concept
On the next request to that site, the browser attaches the cookie on its own — you never write code to do it. The cookie rides up in the Cookie request header.
GET /index.html HTTP/1.1
Host: www.example.com
Cookie: darkmode=trueNow the server sees darkmode=true again and serves the dark theme. The state survived the gap between two independent requests — that's the whole job of a cookie.
Intuition
Picture a coat check. The attendant doesn't memorize your face — they hand you a numbered ticket and keep your coat. The attendant is stateless about you; the ticket carries the state.
A cookie is that ticket. The browser holds it and shows it on every return trip. The server doesn't remember you between visits — it just reads whatever ticket you present.
Ask yourself: who is actually doing the remembering — the attendant or the ticket? (The ticket. If you lose it, the attendant has no idea who you are.)
Ranking
Put in order
Put the moves of §20 Trace one Set-Cookie round trip 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. First contact — the browser has nothing stored for this site, so the request carries no Cookie header.
Worked example
Request 1: browser GETs example.com with no cookie yet
Why: First contact — the browser has nothing stored for this site, so the request carries no Cookie header.
Response 1: server replies with Set-Cookie: darkmode=true
Why: The server asks the browser to store the cookie. The browser saves it associated with this site.
| step | who | header |
|---|---|---|
| request 1 | browser → server | (no Cookie header) |
| response 1 | server → browser | Set-Cookie: darkmode=true |
| browser stores | browser | darkmode=true saved |
| request 2 | browser → server | Cookie: darkmode=true |
Verify: what made request 2 carry the cookie — the page's code or the browser?
Why: §20: the BROWSER auto-attaches it. The server never re-asked; the browser re-sends stored cookies on its own. That automatic re-attachment is exactly what CSRF (L39) abuses.
Discrimination
Sort into buckets
Sort these by who, from memory, without looking back at §20 Trace one Set-Cookie round trip. Telling them apart on the spot is the skill; the table is only where the answer happens to be written down.
Anomaly
Predict first
A student writes this, and it looks reasonable:
A student: 'After I log in, the server just knows it's me on the next request — it keeps my identity around.'
It is wrong. Say what breaks — and say it before you turn the page.
Correct: HTTP is STATELESS — the server has no built-in memory of your previous request.
A student: so how does the server recognize a returning user?
Why: HTTP is STATELESS — the server has no built-in memory of your previous request. By itself it cannot recognize a returning visitor; each request arrives as a stranger.
Trap
A student: 'After I log in, the server just knows it's me on the next request — it keeps my identity around.'
Assume the server statefully tracks who you are
Why: Wrong. HTTP is STATELESS — the server has no built-in memory of your previous request. By itself it cannot recognize a returning visitor; each request arrives as a stranger.
A student: so how does the server recognize a returning user?
The cookie the browser re-sends is what re-identifies you
Why: §20: the server stored a cookie via Set-Cookie; the browser automatically re-attaches it on each request. The COOKIE carries the identity — not the stateless server, and not the connection.
Section
Part 2 · §20.1 Domain · Path · Secure · HttpOnly · expires
Concept
At its core a cookie is just a name=value pair, like darkmode=true or cart=3. Everything else about it — when it's sent, who can read it — is controlled by optional attributes.
Cookie attribute — Extra metadata attached to a cookie in the Set-Cookie header (e.g. Domain, Path, Secure, HttpOnly, expires) that controls when the browser sends the cookie and who is allowed to access it. Attributes are not part of the name=value data.
Concept
Two attributes decide WHICH URLs a cookie is attached to. Domain says which domains; Path says which paths under that domain. Together they scope the cookie to part of the web.
Set-Cookie: sess=abc; Domain=example.com; Path=/accountThis cookie is meant for example.com (and its subdomains) under the /account path. The exact matching rules are §20.2 — coming up next.
Concept
Two flags lock a cookie down. Secure controls the network: the browser only sends the cookie over HTTPS, never plain HTTP. HttpOnly controls JavaScript.
HttpOnly — An attribute that forbids page JavaScript from reading or modifying the cookie (it is invisible to document.cookie). It defends the cookie against theft by injected JavaScript — i.e. against XSS (L40).
So Secure defends the cookie on the wire; HttpOnly defends it from in-page script. They guard two different doors.
Concept
The expires attribute says when the browser should forget the cookie. With an expiry date it's a persistent cookie (survives browser restarts until that date); with none it's a session cookie that vanishes when the browser closes.
| attribute | controls | effect |
|---|---|---|
| Domain | which domains | sent only to a domain-suffix match (§20.2) |
| Path | which paths | sent only when it's a prefix of the URL path |
| Secure | the network | sent only over HTTPS, never plain HTTP |
| HttpOnly | JavaScript | JS cannot read or modify the cookie |
| expires | lifetime | set = persistent until date; absent = dies on browser close |
Comparison
Comparison matrix
From §20.1 expires: session vs persistent: refill the effect column from what you know. The rest of the table is as it appeared.
| attribute | controls | effect |
|---|---|---|
| Domain | which domains | sent only to a domain-suffix match (§20.2) |
| Path | which paths | sent only when it's a prefix of the URL path |
| Secure | the network | sent only over HTTPS, never plain HTTP |
| HttpOnly | JavaScript | JS cannot read or modify the cookie |
| expires | lifetime | set = persistent until date; absent = dies on browser close |
Intuition
Think of the cookie as a note. Secure is shipping it in an armored truck (HTTPS) so nobody reads it in transit. HttpOnly is keeping it in a locked drawer so people in the building (page JavaScript) can't open it.
Two different threats: an eavesdropper on the network vs. malicious script running on the page. Each flag answers exactly one.
Ask yourself: does HttpOnly do anything against a network sniffer? (No — that's Secure's job. HttpOnly only blocks JavaScript.)
Anomaly
Predict first
A student writes this, and it looks reasonable:
A student: 'I set HttpOnly, so now the cookie is encrypted and a network attacker can't read it.'
It is wrong. Say what breaks — and say it before you turn the page.
Correct: HttpOnly does NOT encrypt anything and does nothing on the network.
A student: which flag protects the cookie on the network?
Why: HttpOnly does NOT encrypt anything and does nothing on the network. A sniffer on a plain-HTTP connection still reads an HttpOnly cookie in the clear. HttpOnly only blocks one thing: JavaScript access.
Trap
A student: 'I set HttpOnly, so now the cookie is encrypted and a network attacker can't read it.'
Treat HttpOnly as network/crypto protection
Why: Wrong. HttpOnly does NOT encrypt anything and does nothing on the network. A sniffer on a plain-HTTP connection still reads an HttpOnly cookie in the clear. HttpOnly only blocks one thing: JavaScript access.
A student: which flag protects the cookie on the network?
Secure protects the network; HttpOnly only blocks JavaScript
Why: §20.1: Secure makes the browser send the cookie only over HTTPS (vs sniffers). HttpOnly hides the cookie from page JS (vs XSS, L40). Different doors — you usually want both on a session cookie.
Pattern
Predict first
The table runs: name=value | sid=42 | the cookie's data · Domain | example.com | sent to example.com and its subdomains · Path | / | sent to every path on that domain · Secure | (flag) | sent only over HTTPS, never plain HTTP
In §20.1 Read every attribute on one Set-Cookie, given the rows so far: what is the next one — the row where attribute is HttpOnly?
Correct: HttpOnly | (flag) | page JavaScript cannot read or modify it
| attribute | value | what it means here |
|---|---|---|
| name=value | sid=42 | the cookie's data |
| Domain | example.com | sent to example.com and its subdomains |
| Path | / | sent to every path on that domain |
| Secure | (flag) | sent only over HTTPS, never plain HTTP |
| HttpOnly | (flag) | page JavaScript cannot read or modify it |
Why: The relationship between the columns, not the individual numbers, is what generates the next row. Read each attribute and state what it controls about this cookie.
Worked example
Take Set-Cookie: sid=42; Domain=example.com; Path=/; Secure; HttpOnly
Why: Read each attribute and state what it controls about this cookie.
| attribute | value | what it means here |
|---|---|---|
| name=value | sid=42 | the cookie's data |
| Domain | example.com | sent to example.com and its subdomains |
| Path | / | sent to every path on that domain |
| Secure | (flag) | sent only over HTTPS, never plain HTTP |
| HttpOnly | (flag) | page JavaScript cannot read or modify it |
Verify: a sniffer on a plain-HTTP request to example.com — do they see sid=42?
Why: §20.1: no. Secure means the browser never attaches the cookie over plain HTTP, so it simply isn't on that request to sniff. (If Secure were absent, they would see it — HttpOnly would not help, since HttpOnly only blocks JavaScript.)
Trade off
Comparison matrix
From §20.1 Read every attribute on one Set-Cookie: every row here is a choice with a cost. Fill the what it means here column, then say which row you would actually pick and what you give up for it.
| attribute | value | what it means here |
|---|---|---|
| name=value | sid=42 | the cookie's data |
| Domain | example.com | sent to example.com and its subdomains |
| Path | / | sent to every path on that domain |
| Secure | (flag) | sent only over HTTPS, never plain HTTP |
| HttpOnly | (flag) | page JavaScript cannot read or modify it |
Section
Part 3 · §20.2 when a cookie is SENT
Concept
Scenario: the browser is about to make a request to some URL and must decide whether a stored cookie goes along. The rule has two conditions, both required.
The browser sends the cookie to a URL iff the cookie's Domain is a domain-SUFFIX of the URL's domain AND the cookie's Path is a PREFIX of the URL's path.
Concept
The asymmetry matters. Domains are read right to left (the suffix is the shared tail), so example.com is a suffix of foo.example.com. Paths are read left to right (the prefix is the shared head), so /some/path is a prefix of /some/path/index.html.
Domain (suffix, right→left): foo.example.com ends with example.com ✓
Path (prefix, left→right): /some/path/index.html starts with /some/path ✓Concept
Take a cookie with Domain=example.com and Path=/some/path. Is it sent to http://foo.example.com/some/path/index.html?
cookie: Domain=example.com Path=/some/path
URL: http://foo.example.com/some/path/index.html
is example.com a suffix of foo.example.com ? YES
is /some/path a prefix of /some/path/index.html ? YESBoth conditions hold, so the cookie IS sent. The URL's domain ends in the cookie domain, and the URL's path begins with the cookie path.
Sorting
Sort into buckets
These are the pieces of L38 · Cookies & Session Management, out of order. Put each one back under the part of the lesson it belongs to.
Intuition
Domain names get more specific as you go left: mail.foo.example.com is more specific than example.com. So a cookie scoped to the broad tail (example.com) should reach every more-specific name under it — that's a suffix match.
Paths get more specific as you go right: /some/path/index.html is under /some/path. So a cookie scoped to a broad head should reach everything beneath it — that's a prefix match.
Ask yourself: would a cookie for Domain=foo.example.com be sent to plain example.com? (No — foo.example.com is NOT a suffix of example.com; it's the other way around.)
Worked example
Cookie: Domain=example.com, Path=/some/path
Why: Apply both tests to each URL: domain-suffix AND path-prefix. Both must pass.
| URL | domain suffix? | path prefix? | sent? |
|---|---|---|---|
| http://foo.example.com/some/path/x.html | yes | yes | SENT |
| http://example.com/some/path/ | yes | yes | SENT |
| http://example.com/other | yes | NO | not sent |
| http://evil.com/some/path | NO | yes | not sent |
| http://attackerexample.com/some/path | NO | yes | not sent |
Verify the last row: why isn't attackerexample.com a suffix match?
Why: §20.2: suffix matching is on DOMAIN LABELS, not raw string endings. attackerexample.com does not end at a dot boundary of example.com — its rightmost label component is attackerexample, not example. So the domain test fails and the cookie is NOT sent.
Comparison
Comparison matrix
From §20.2 Which URLs receive the cookie?: refill the domain suffix? column from what you know. The rest of the table is as it appeared.
| URL | domain suffix? | path prefix? | sent? |
|---|---|---|---|
| http://foo.example.com/some/path/x.html | yes | yes | SENT |
| http://example.com/some/path/ | yes | yes | SENT |
| http://example.com/other | yes | NO | not sent |
| http://evil.com/some/path | NO | yes | not sent |
| http://attackerexample.com/some/path | NO | yes | not sent |
Anomaly
Predict first
A student writes this, and it looks reasonable:
A student: 'Cookies are scoped by origin just like the SOP — same scheme, host, and port.'
It is wrong. Say what breaks — and say it before you turn the page.
Correct: Cookie policy is NOT origin matching.
A student: what rule actually governs when a cookie is sent?
Why: Cookie policy is NOT origin matching. It uses domain-SUFFIX + path-PREFIX. A cookie for example.com is sent to foo.example.com even though those are different origins — the SOP would never share across them.
Trap
A student: 'Cookies are scoped by origin just like the SOP — same scheme, host, and port.'
Apply origin matching (exact host) to cookies
Why: Wrong. Cookie policy is NOT origin matching. It uses domain-SUFFIX + path-PREFIX. A cookie for example.com is sent to foo.example.com even though those are different origins — the SOP would never share across them.
A student: what rule actually governs when a cookie is sent?
Domain-suffix + path-prefix — NOT origin matching
Why: §20.2: the cookie goes to any URL whose domain ends in the cookie's Domain and whose path starts with the cookie's Path. The mismatch between this and the SOP (L37) has caused real-world bugs — it's the heart of §20.5.
Section
Part 4 · §20.3 what a server may SET
Concept
Scenario: if any server could set a cookie for any domain, evil.com could forge a bank.com session cookie. The browser must restrict what Domain a response is allowed to claim.
The rule: a server may only set a cookie whose Domain is a SUFFIX of the server's own URL. Path may be set without restriction.
Concept
eecs.berkeley.edu may set a cookie for eecs.berkeley.edu or for berkeley.edu — its own URL ends in both of those. It may NOT set a cookie for some unrelated domain.
server: eecs.berkeley.edu
Domain=eecs.berkeley.edu ✓ (suffix of itself)
Domain=berkeley.edu ✓ (eecs.berkeley.edu ends in berkeley.edu)
Domain=stanford.edu ✗ (not a suffix of the server)
Domain=cs.berkeley.edu ✗ (server is not under cs.berkeley.edu)Concept
There's a crucial exception: you cannot set a cookie on a top-level domain like .com or .edu. It's too broad — a .com cookie would be sent to every .com site on the web.
Browsers keep a list of these forbidden suffixes — including two-level ones like .co.uk. So even though berkeley.edu ends in .edu, no server may set a cookie with Domain=.edu.
Intuition
Think of the domain name as a branch of a tree, read right to left. A server sits at one node and may set a cookie for itself or any node above it on its own branch — but never jump to a different branch, and never all the way to the root (the TLD).
Setting Domain=berkeley.edu from eecs.berkeley.edu is widening up your own branch (fine). Setting Domain=.edu is grabbing the whole forest (forbidden).
Ask yourself: why is .co.uk on the forbidden list even though it has two labels? (Because it functions as a TLD — millions of unrelated sites live under it, so a .co.uk cookie would leak across all of them.)
Explain it
Discussion prompt
Explain §20.3 You can only widen up your own branch 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:
Setting Domain=berkeley.edu from eecs.berkeley.edu is widening up your own branch (fine). Setting Domain=.edu is grabbing the whole forest (forbidden).
Pattern
Predict first
The table runs: eecs.berkeley.edu | yes | no | ALLOWED · berkeley.edu | yes | no | ALLOWED · .edu | yes | YES (TLD) | forbidden · cs.berkeley.edu | no | no | forbidden
In §20.3 Which Domain values may eecs.berkeley.edu set?, given the rows so far: what is the next one — the row where candidate Domain is stanford.edu?
Correct: stanford.edu | no | no | forbidden
| candidate Domain | suffix of server? | is it a TLD? | allowed? |
|---|---|---|---|
| eecs.berkeley.edu | yes | no | ALLOWED |
| berkeley.edu | yes | no | ALLOWED |
| .edu | yes | YES (TLD) | forbidden |
| cs.berkeley.edu | no | no | forbidden |
| stanford.edu | no | no | forbidden |
Why: The relationship between the columns, not the individual numbers, is what generates the next row. A Domain is allowed iff it is a suffix of the server's URL AND it is not a public suffix / TLD.
Worked example
Server URL is eecs.berkeley.edu; test each candidate Domain
Why: A Domain is allowed iff it is a suffix of the server's URL AND it is not a public suffix / TLD.
| candidate Domain | suffix of server? | is it a TLD? | allowed? |
|---|---|---|---|
| eecs.berkeley.edu | yes | no | ALLOWED |
| berkeley.edu | yes | no | ALLOWED |
| .edu | yes | YES (TLD) | forbidden |
| cs.berkeley.edu | no | no | forbidden |
| stanford.edu | no | no | forbidden |
Verify the .edu row: it IS a suffix, so why is it still forbidden?
Why: §20.3: suffix-of-server is necessary but not sufficient. The TLD exception overrides it — .edu is a public suffix, so it's blocked despite being a valid suffix. The two rules apply together.
Trade off
Comparison matrix
From §20.3 Which Domain values may eecs.berkeley.edu set?: every row here is a choice with a cost. Fill the allowed? column, then say which row you would actually pick and what you give up for it.
| candidate Domain | suffix of server? | is it a TLD? | allowed? |
|---|---|---|---|
| eecs.berkeley.edu | yes | no | ALLOWED |
| berkeley.edu | yes | no | ALLOWED |
| .edu | yes | YES (TLD) | forbidden |
| cs.berkeley.edu | no | no | forbidden |
| stanford.edu | no | no | forbidden |
Anomaly
Predict first
A student writes this, and it looks reasonable:
A student: 'A response can carry Set-Cookie with whatever Domain it likes — evil.com could set a bank.com cookie.'
It is wrong. Say what breaks — and say it before you turn the page.
Correct: The browser rejects a Set-Cookie whose Domain is not a suffix of the responding server's own URL.
A student: what Domain values is a server actually allowed to set?
Why: The browser rejects a Set-Cookie whose Domain is not a suffix of the responding server's own URL. evil.com cannot set a bank.com cookie — bank.com is not a suffix of evil.com. Nor can anyone set a cookie on a bare TLD.
Trap
A student: 'A response can carry Set-Cookie with whatever Domain it likes — evil.com could set a bank.com cookie.'
Assume Domain on Set-Cookie is unrestricted
Why: Wrong. The browser rejects a Set-Cookie whose Domain is not a suffix of the responding server's own URL. evil.com cannot set a bank.com cookie — bank.com is not a suffix of evil.com. Nor can anyone set a cookie on a bare TLD.
A student: what Domain values is a server actually allowed to set?
Only a suffix of its own URL, and never a bare TLD
Why: §20.3: eecs.berkeley.edu may set eecs.berkeley.edu or berkeley.edu (suffixes of itself), never an unrelated domain, never .edu. This is exactly what stops cross-site cookie forgery — but note it still allows the §20.5 cross-subdomain trick.
Section
Part 5 · §20.4–20.5 tokens & shared cookies
Concept
Scenario: you POST your username and password to /login. On a valid login the server generates a random session token and sends it back as a cookie. From then on, the cookie is your proof of being logged in.
Session token — A random, unpredictable value the server issues on login and stores as a cookie. The browser re-attaches it on each request; the server maps token → user to know who is logged in without re-checking the password every time.
Concept
The mechanism is just the cookie machinery from Part 1. The server sets the token; the browser auto-sends it on every request; the server looks up which user that token belongs to.
Set-Cookie: session=9f3a...e21; HttpOnly; Secure ← on login
Cookie: session=9f3a...e21 ← every later requestThe server keeps a table token → user. Seeing session=9f3a...e21 again, it knows it's Alice — no password re-entry needed.
Analogy
Discussion prompt
Explain §20.4 The browser re-attaches it; the server maps 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:
The mechanism is just the cookie machinery from Part 1. The server sets the token; the browser auto-sends it on every request; the server looks up which user that token belongs to.
Concept
If an attacker could guess a valid session token, they'd be logged in as that user — no password required. So tokens must be random and unpredictable, drawn from a cryptographically strong source.
They're also usually flagged HttpOnly (so injected JavaScript can't steal them — vs XSS, L40) and Secure (so a network sniffer can't read them off a plain-HTTP request). The token is high-value, so it gets both locks.
Intuition
Don't think of 'cookie' and 'session token' as two separate things. A session token IS a cookie — a particularly important one. The two words name two different layers.
'Cookie' = the transport mechanism: how the browser stores and re-sends the value. 'Session token' = the meaning of the value: the random string that identifies the logged-in user.
Ask yourself: if the session-token cookie isn't HttpOnly and an attacker injects JS, what happens? (The script reads the token and the attacker can impersonate you — session hijacking via XSS.)
Counterexample
Discussion prompt
Don't think of 'cookie' and 'session token' as two separate things. A session token IS a cookie — a particularly important one. The two words name two different layers.
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.
Answer:
Ask yourself: if the session-token cookie isn't HttpOnly and an attacker injects JS, what happens? (The script reads the token and the attacker can impersonate you — session hijacking via XSS.)
Ranking
Put in order
Put the moves of §20.4 Trace a login and a follow-up request 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. Valid login — the server is about to mint a fresh, random session token for her.
Worked example
Alice POSTs valid credentials to /login
Why: Valid login — the server is about to mint a fresh, random session token for her.
Server replies Set-Cookie: session=9f3a...e21; HttpOnly; Secure
Why: The random token is stored as a hardened cookie: HttpOnly (no JS access) and Secure (HTTPS only).
| step | header | server's view |
|---|---|---|
| POST /login | username=alice&password=… | verify password |
| response | Set-Cookie: session=9f3a...e21 | record token→alice |
| GET /account | Cookie: session=9f3a...e21 | look up token → alice |
| GET /settings | Cookie: session=9f3a...e21 | look up token → alice |
Verify: did the browser re-send the password on /account and /settings?
Why: §20.4: no — only the session token rides along, auto-attached by the browser. The server maps token→alice each time. This is why a stolen token alone is enough to impersonate her (session hijacking).
Comparison
Comparison matrix
From §20.4 Trace a login and a follow-up request: refill the server's view column from what you know. The rest of the table is as it appeared.
| step | header | server's view |
|---|---|---|
| POST /login | username=alice&password=… | verify password |
| response | Set-Cookie: session=9f3a...e21 | record token→alice |
| GET /account | Cookie: session=9f3a...e21 | look up token → alice |
| GET /settings | Cookie: session=9f3a...e21 | look up token → alice |
Concept
Here is the lesson's crux. The cookie send/set rules are domain-suffix based; the same-origin policy (L37) is exact-origin based (scheme + host + port). These are different policies.
The consequence: two different origins can share a cookie. A cookie scoped to berkeley.edu is readable and writable by BOTH eecs.berkeley.edu and auth.berkeley.edu — even though, under the SOP, those are distinct origins that cannot read each other's pages.
Concept
Now the danger. Suppose an attacker controls eecs.berkeley.edu. By §20.3 they may set a cookie with Domain=berkeley.edu (a suffix of their own URL). By §20.2 the browser then sends that cookie to auth.berkeley.edu too.
attacker @ eecs.berkeley.edu → Set-Cookie: session=ATTACKER; Domain=berkeley.edu
browser later visits auth.berkeley.edu
→ Cookie: session=ATTACKER (suffix match: berkeley.edu ⊑ auth.berkeley.edu)So the attacker injected a cookie into a different origin's requests. The SOP never permitted eecs to touch auth's pages — but the cookie policy let a berkeley.edu cookie cross between them. That gap is the §20.5 attack.
Ranking
Put in order
Put the moves of §20.5 Walk the cross-subdomain cookie injection 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. §20.3 permits it: berkeley.edu is a suffix of eecs.berkeley.edu (and it's not a TLD), so the browser accepts the Set-Cookie.
Worked example
Attacker controls eecs.berkeley.edu and sets Domain=berkeley.edu cookie
Why: §20.3 permits it: berkeley.edu is a suffix of eecs.berkeley.edu (and it's not a TLD), so the browser accepts the Set-Cookie.
Victim's browser stores the berkeley.edu cookie
Why: The cookie is now associated with the broad domain berkeley.edu, not just eecs.
| question | SOP (origins) | cookie policy (suffix) |
|---|---|---|
| can eecs read auth's PAGE? | NO (different origin) | n/a |
| can eecs set a berkeley.edu cookie? | n/a | YES (suffix of itself) |
| is that cookie sent to auth.berkeley.edu? | n/a | YES (suffix match) |
| net effect | — | eecs injected a cookie into auth's requests |
Verify the mismatch: the SOP blocks page access but the cookie still crosses
Why: §20.5: the two policies disagree. SOP isolates the origins; cookie policy (domain-suffix) does NOT. The cookie crosses an origin boundary the SOP would never open — that's the whole point of §20.5.
Discrimination
Sort into buckets
Sort these by SOP (origins), from memory, without looking back at §20.5 Walk the cross-subdomain cookie injection. Telling them apart on the spot is the skill; the table is only where the answer happens to be written down.
Concept
⊕ Supplemental — beyond §20.1–20.5. A few practical session topics you'll meet when we attack and defend logins.
Anomaly
Predict first
A student writes this, and it looks reasonable:
A student: 'eecs.berkeley.edu and auth.berkeley.edu are different origins, so by isolation they can never share a cookie.'
It is wrong. Say what breaks — and say it before you turn the page.
Correct: Cookie policy is domain-SUFFIX, not origin.
A student: when DO two different origins share a cookie?
Why: Cookie policy is domain-SUFFIX, not origin. A cookie scoped to berkeley.edu is shared by both subdomains — different origins, same cookie. Origin isolation (SOP) governs page access, not cookie scope.
Trap
A student: 'eecs.berkeley.edu and auth.berkeley.edu are different origins, so by isolation they can never share a cookie.'
Assume cookie sharing follows origin isolation
Why: Wrong. Cookie policy is domain-SUFFIX, not origin. A cookie scoped to berkeley.edu is shared by both subdomains — different origins, same cookie. Origin isolation (SOP) governs page access, not cookie scope.
A student: when DO two different origins share a cookie?
Whenever a domain-suffix cookie covers both of them
Why: §20.5: a berkeley.edu cookie is read/written by every *.berkeley.edu origin. That's the gap — an attacker on eecs.berkeley.edu can inject a berkeley.edu cookie the browser then sends to auth.berkeley.edu. Different origins, shared cookie.
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.
darkmode=true is the cookie. The browser reads the Set-Cookie header and stores the cookie locally, associated with this site.; On the next request to that site, the browser attaches the cookie on its own — you never write code to do it. The cookie rides up in the Cookie request header.; A cookie is that ticket. The browser holds it and shows it on every return trip. The server doesn't remember you between visits — it just reads whatever ticket you present.Constraint
Discussion prompt
Run The cookie & session playbook with this step confiscated:
Set rule (§20.3): a server may set a cookie only for a Domain that is a SUFFIX of its own URL — never an unrelated domain, never a bare TLD (.com, .edu, .co.uk). Path is unrestricted.
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:
Set-Cookie and the browser AUTOMATICALLY re-attaches the cookie on later requests. The cookie carries…Domain/Path scope where it's sent; Secure = HTTPS-only (vs network); HttpOnly = no JavaScript access (vs XSS); expires = persistent…Domain is a domain-SUFFIX of the URL's domain AND cookie Path is a PREFIX of the URL's path. Suffix right→left on…Domain that is a SUFFIX of its own URL — never an unrelated domain, never a bare TLD (.com…HttpOnly + Secure); the browser re-sends it…Pattern
Set-Cookie and the browser AUTOMATICALLY re-attaches the cookie on later requests. The cookie carries the state, not the server.Domain/Path scope where it's sent; Secure = HTTPS-only (vs network); HttpOnly = no JavaScript access (vs XSS); expires = persistent vs session lifetime.Domain is a domain-SUFFIX of the URL's domain AND cookie Path is a PREFIX of the URL's path. Suffix right→left on the domain, prefix left→right on the path.Domain that is a SUFFIX of its own URL — never an unrelated domain, never a bare TLD (.com, .edu, .co.uk). Path is unrestricted.HttpOnly + Secure); the browser re-sends it; the server maps token→user. A session token IS a cookie.Edge cases
Discussion prompt
The cookie & session 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:
Set-Cookie and the browser AUTOMATICALLY re-attaches the cookie on later requests. The cookie carries…Domain/Path scope where it's sent; Secure = HTTPS-only (vs network); HttpOnly = no JavaScript access (vs XSS); expires = persistent…Domain is a domain-SUFFIX of the URL's domain AND cookie Path is a PREFIX of the URL's path. Suffix right→left on…Domain that is a SUFFIX of its own URL — never an unrelated domain, never a bare TLD (.com…HttpOnly + Secure); the browser re-sends it…Elimination
Eliminate the wrong options
To which request URL does the browser send the cookie (Domain=example.com, Path=/account)?
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: A
Why: §20.2: the cookie is sent iff its Domain is a domain-suffix of the URL's domain AND its Path is a prefix of the URL's path. For app.example.com/account/settings both hold: example.com is a suffix of app.example.com, and /account is a prefix of /account/settings — so the cookie is sent. B fails the path test (/about does not start with /account). C and D fail the domain test: example.com.evil.com is under evil.com (not a suffix of example.com), and otherexample.com does not end at a label boundary of example.com.
Check
A cookie was stored with Domain=example.com and Path=/account. Before clicking, apply the §20.2 send rule — domain-SUFFIX and path-PREFIX, both required — to each candidate URL.
Check your understanding
To which request URL does the browser send the cookie (Domain=example.com, Path=/account)?
Answer: A
Why: §20.2: the cookie is sent iff its Domain is a domain-suffix of the URL's domain AND its Path is a prefix of the URL's path. For app.example.com/account/settings both hold: example.com is a suffix of app.example.com, and /account is a prefix of /account/settings — so the cookie is sent. B fails the path test (/about does not start with /account). C and D fail the domain test: example.com.evil.com is under evil.com (not a suffix of example.com), and otherexample.com does not end at a label boundary of example.com.
Concept
Secure is what protects the cookie on the network.Concept
HttpOnly.Concept
Connect it up
Draw it
One page, no notation unless you need it: draw how these connect — Why Cookies Exist — Statelessness · Cookie Attributes — Who Can Touch It · The Send Rule — Suffix + Prefix · The Set Rule — Suffix of the Server · Sessions & the Cookie-vs-SOP Gap. Put an arrow wherever one of them is what makes another possible, and label the arrow with why.
Recap
You can now explain why stateless HTTP forces cookies and trace a Set-Cookie round trip, name what Domain/Path/Secure/HttpOnly/expires each control, apply the send rule (domain-suffix + path-prefix) and the set rule (suffix-of-server, never a TLD), and explain session tokens plus the §20.5 gap where cookie policy ≠ SOP lets two different origins share a cookie.
| Idea | § | The one-line version |
|---|---|---|
| Why cookies | 20 | HTTP is stateless; the browser auto-re-sends a cookie to carry state |
| Attributes | 20.1 | Domain/Path scope · Secure=HTTPS · HttpOnly=no JS · expires=lifetime |
| Send rule | 20.2 | send iff Domain is a domain-SUFFIX and Path is a PREFIX of the URL |
| Set rule | 20.3 | set only a suffix of your own URL; never a bare TLD (.com/.edu/.co.uk) |
| Session token | 20.4 | random unpredictable cookie; server maps token→user; HttpOnly+Secure |
| Cookie vs SOP | 20.5 | suffix-based, not origin — two different origins CAN share a cookie |
| The attack | 20.5 | eecs sets a berkeley.edu cookie the browser sends to auth.berkeley.edu |
| Bridge | — | cookies → CSRF (L39); non-HttpOnly cookies → XSS theft (L40) |
Want this taught 1-on-1? Alexander tutors Computer Security — $55/session, free consultation.