skip to content

Identifier Issuance & Signing

What actually travels in the session cookie: an unguessable reference to server state, or the state itself with a signature over it. Interviewers probe entropy sizing and how the signing key rotates.

on this pageshow

questions

4

A playout scheduler's session cookie carries `presenter=dmorgan; role=engineer` in plain text — what stops a presenter becoming the engineer?

level: juniorimportance: must knowfreq 72%

answer

  1. a cookie value is input
  2. the caller can retype it
  3. a reference, not a verdict
  4. unguessable value naming a server record

basics

~20 s

Nothing stops them. A cookie value is input the caller can retype, so a plain-text role claim is simply edited. The value must be an unguessable reference to a record the scheduler holds, or state the server can prove it wrote.

solid answer

~40 s

A `Set-Cookie` header asks the caller's browser to store a string; the storage belongs to the caller, and the next request presents whatever is in it. So `role=engineer` is a claim the caller makes about themselves, not a fact the scheduler established — a presenter edits it and inherits the duty engineer's authority. The default fix is an **opaque reference**: a long unpredictable value that names a server-side record holding the account and its roles, so the cookie asserts nothing and there is nothing in it to forge. The alternative — keeping the claims in the cookie — is legitimate only when a tag computed with a server-held key covers them, which is a separate decision. Encoding the text, or swapping the name for the account's row id, changes nothing.

code

http · 7 lines
http
# the claim the caller can retype
HTTP/1.1 302 Found
Set-Cookie: sched=presenter%3Ddmorgan%26role%3Dengineer; Path=/; Secure; HttpOnly

# the reference that names a record only the server can read
HTTP/1.1 302 Found
Set-Cookie: sched=9Qb2rL7xK0mVd8sYtA3pZw; Path=/; Secure; HttpOnly

go deeper

for a junior

Recall one sentence: a cookie value is input the caller can retype. From that, the value must be an unguessable reference to something the server holds, never the server's conclusion about who the caller is.

for a middle

Explain why encoding and row ids fix nothing, and name the second legitimate shape — claims covered by a tag computed with a server-held key — along with the lookup you trade away by choosing it.

for a senior

Show the issuance path end to end: draw the value from a cryptographically secure generator, write the record, set the cookie, and treat an unknown value as unauthenticated rather than as an error worth reporting to the caller.

for a principal

Frame it as where authority lives. A referenced session keeps every decision revocable on your side; an asserted one moves the decision into the caller's storage and buys throughput with it. Say which your service can afford before anyone writes code.

A session cookie is the only thing connecting one HTTP request to the last one. Everything the scheduler believes about the caller has to be recoverable from the value in that cookie, so the whole design question is what that value should be. ## The cookie is caller-supplied input A `Set-Cookie` header is a request to store a string, and the storage is the caller's, not the server's. The browser keeps it where the presenter can open it, and the next request presents whatever is there in a `Cookie` header. Nothing accompanies the value to say *this is the string you issued*. A cookie value therefore carries exactly the trust level of a form field or a query parameter: **it is input**. That makes `presenter=dmorgan; role=engineer` a claim the caller makes about themselves. A presenter who edits `role` and reloads is not exploiting a bug; they are using the protocol as designed. The scheduler cannot distinguish the string it wrote from the string that came back, because nothing in the value is tied to the act of issuing it. Hiding the text does not change this. Base64, hex, a reversed string or a private encoding are all transformations anyone can undo and redo. Obscurity of format is not integrity. ## Two shapes that survive an edit - **An opaque reference.** The cookie holds a long unpredictable value and nothing else. The scheduler files a record under that value holding the account, its roles and the roster it may touch. When the cookie arrives, the server looks the record up; no record means an unauthenticated caller. RFC 6265 Section 8.4 describes exactly this — a **session identifier (nonce)** whose only use is interacting with the server — which is why reading one teaches an attacker nothing beyond the ability to replay it. - **State the server can prove it wrote.** The cookie holds the claims themselves plus a tag computed over them with a key only the server has, so an altered value fails verification. This is a real design with real trade-offs, and choosing between signing that state and encrypting it is a decision of its own. | what the cookie holds | caller can read it | caller can alter it undetected | server lookup per request | |---|---|---|---| | a plain claim (`role=engineer`) | yes | **yes** | none | | an opaque reference | reading it teaches nothing | no — there is nothing in it to alter | one | | signed state | yes | no | none | ## Issuing the reference 1. Draw a long random value from a cryptographically secure generator — never a counter, a timestamp, the account's row id, or a digest of any of those. 2. Write the server-side record under that value with everything a handler will need: account, roles, and when it was issued. 3. Set the cookie with that value and nothing else inside it. Step 1 is where this leaf's other question lives: how many bits that value needs, and how you derive the number instead of quoting one. ## Why the account id is not a shortcut Replacing the name with the account's numeric row id looks tidier and fixes nothing. It is still a claim, and it is now a **guessable** claim: sequential ids let a caller walk the whole population by arithmetic, and the caller who lands on the duty engineer's id inherits the engineer's authority. The defect was never that the value was readable. It was that the value was *asserted* rather than *referenced*. ## What an opaque reference still does not fix - It is a **bearer** value: whoever presents it is treated as that presenter, so a copy taken off a shared studio machine works until the record is destroyed. - It says nothing about intent — a request arriving with a valid cookie is authenticated, which is not the same as wanted. - It does not decide where the record lives, how long it survives, or what happens at the next sign-in; those are separate decisions with their own failure modes. ## What interviewers are listening for The junior-band answer is that a cookie is user input, so its value must be a reference rather than a verdict. The stronger answer names the exception honestly: you *may* keep the verdict in the cookie, but only under a tag, and you should be able to say what that buys and what it costs. A candidate who answers "the cookie is `HttpOnly`, so it cannot be changed" has confused keeping script away from a value with keeping its owner away from it, and that single confusion is the most common way this question is failed.

  • The team replaces the name and role with the presenter's account row id. Has anything improved?
    No. It is still a claim the caller supplies, and now a guessable one — row ids are sequential, so a presenter can try the ids around their own until one belongs to the duty engineer. The fix is not a tidier claim but an unguessable value that asserts nothing and names a server-side record.
  • If the record lives on the server, what is the cookie value actually good for?
    Exactly one job: naming that record unguessably. It carries no meaning, so there is nothing in it to alter, nothing to leak by reading it, and nothing to keep consistent with the record. That single job is what makes its length the only property worth arguing about.
  • Is putting the roles in the cookie ever the right answer?
    Yes, when a tag computed with a server-held key covers them, which makes an edit detectable. You then trade a lookup per request for a value you cannot withdraw before it expires without keeping the server state you were avoiding — and you must decide separately whether the contents are safe for the caller to read.

A cloakroom ticket versus a note reading "the coat in bay four is mine". The ticket is meaningless to read and useless to edit — the attendant's book decides what it redeems. The note decides for itself, so anyone can write a better one.

saying these in an interview costs you the question

  • The cookie is HttpOnly, so its owner cannot change the value
  • Base64 or a private encoding makes the claim safe to send
  • Nobody would bother opening a cookie file and editing it
  • Use the account row id — ids are unique, so they are safe
  • Signing it is an optimisation we can add later if abused
open as a page

How many random bits does the session identifier in a cookie need, and how do you derive that number?

level: middleimportance: must knowfreq 60%

basics

~20 s

Derive it from four measured quantities: live sessions, the guess rate your front door permits, the window identifiers stay valid, and the hit probability you accept. Expected hits are about N x R x T / 2^B, which puts the floor near 64 bits and the norm at 128.

open as a page

A scheduler's session cookie carries the presenter's identity and roster instead of a server-side record — when does signing that value suffice, and when must it be encrypted?

level: seniorimportance: should knowfreq 42%

basics

~20 s

Signing stops alteration but not reading, so it suffices only when the contents are safe for the holder to see. Encrypt — with authenticated encryption, never bare ciphertext — when the roster or the roles are themselves worth hiding. Both replay until they expire.

open as a page

How do you roll the key that signs a scheduler's session cookies without signing out every presenter in the same minute?

level: seniorimportance: should knowfreq 38%

basics

~20 s

Overlap in a strict order: every instance must accept the new key for verification before any instance signs with it, and the retiring key stays in the verification set for at least the longest remaining lifetime of anything it signed.

open as a page