Where a browser client keeps an access token between requests decides which attack wins — which attack goes with which resting place?
answer
- custody picks the attacker
- readable by script, or sent automatically
- injection steals; automatic attachment forges
- a stolen token is portable
- neither is safer in the abstract
basics
~20 sA token in script-readable storage loses to script injection; a token in a cookie the browser attaches automatically loses to cross-site forged requests. Neither is safer until you name the threat you are buying down.
solid answer
~50 sCustody picks your attacker. If the operations-room page keeps its access token where its own script can read it, any injected script — a dependency that shipped a bad release, a note rendered without escaping — reads it and posts it somewhere the attacker controls. That token is portable: it keeps working from the attacker's own machine, with no victim involved, until `exp`. If instead it rides in a cookie the browser attaches on its own, injected script cannot read it — but automatic attachment is indiscriminate, so a page on an unrelated origin can cause the browser to send a request in the user's name. That choice creates an obligation: you now owe a cross-site request-forgery defence on state-changing endpoints. A third resting place, a variable and nowhere else, dies on reload and stops nothing.
go deeper
Learn the two sentences and say them in this order: script-readable storage loses to injected script, a browser-attached cookie loses to a forged cross-site request. Getting both losers right is the whole junior answer.
Explain the mechanism behind each loser — why a document runs code nobody reviewed, and why automatic attachment fires on the destination rather than on the sender. Then name the obligation the cookie choice creates.
Refuse the "more secure" framing out loud and pick a side for the application in front of you, citing how much foreign code the page runs and whether you already have one place to put the forgery defence.
Frame it as which failure you are prepared to own: a silent permanent credential loss, or a bounded stream of forged actions against endpoints you must now enumerate and defend. Say which your organisation can actually detect.
## Custody is a choice about who your attacker is A bearer credential is a string, and whoever holds it is the user. So the only interesting question about an access token sitting on a client is **where it rests between requests**, because the resting place decides which class of attacker can reach it. There is no "secure" answer in the abstract. There is an answer the moment you name the threat. The turbine maintenance scheduler here is opened two ways: from a browser in the operations room, and from a native application on a tablet at the base of a tower with a glove on. The browser case is where the classic trade lives, because a browser page is the one client that executes code you did not write. ## The two resting places and their named losers **Script-readable storage.** The page keeps the access token somewhere its own script can read — a browser storage slot, a variable, an in-page cache. Any script the document runs can read it, and a document runs a lot of script nobody on your team wrote: a transitive dependency that shipped a bad release, a vendor tag, a stored maintenance note rendered without escaping. That is script injection, and when it succeeds the token is **exfiltrated** — copied out to a host the attacker controls. It is portable. It keeps working from the attacker's own machine, with no browser and no victim, until it expires. **A cookie the browser attaches on its own.** The credential goes in a cookie that script cannot read (an `HttpOnly` flag is what makes it so). Injected script now cannot copy it out. But automatic attachment is indiscriminate: the browser attaches the cookie because the request is going to your host, not because your page made the request. A page on an unrelated origin can therefore cause a request to be sent in the user's name. That is cross-site request forgery, and this custody choice creates an **obligation** — you now owe a forgery defence on every state-changing endpoint. What that defence is and where it runs is a separate design question. The point here is that the obligation is created by the custody choice, not by the endpoint. | | script-readable storage | browser-attached cookie | |---|---|---| | reachable by injected script | yes — readable and exfiltratable | no | | sent by an unrelated origin's page | no | yes, automatically | | loses to | script injection | cross-site request forgery | | obligation it creates | keep foreign script out of the page | defend state-changing endpoints | | after the tab closes | attacker still holds a working credential | attacker holds nothing | ## Why "which one is safer" is the wrong question The asymmetry in that table is the whole lesson. The two options do not sit on one axis. One loses a credential permanently and silently; the other loses individual actions, only while the victim is on your site, and only at endpoints you failed to defend. Which failure you prefer depends on facts about your own application: - **How much foreign code does the page execute?** A page with a large dependency graph and third-party embeds has a wide injection surface and has no business holding a portable credential. - **How long is the credential good for, and what can it do?** A short-lived, narrowly scoped access token is a much smaller prize than a long-lived broad one. - **Do you have somewhere to put the forgery defence?** If every state change already passes through one place, the cookie obligation is cheap. If it does not, it is not cheap, and pretending otherwise is how the obligation goes unpaid. ## The third resting place people forget You can also keep the token **in a variable and nowhere else**, with no persistence at all. It is not a free win. It dies on a page reload and does not exist in a second tab, so the application needs a way to obtain a fresh one on demand, which is its own machinery. And injected script running in the same document reads a variable exactly as easily as it reads a storage slot, so this choice buys nothing against injection. What it buys is narrower and real: the credential does not outlive the document. ## What custody does not change Custody is a client-side decision, and it does not change what the server does when a credential arrives. The same verification runs whichever way it was carried. A request with no usable credential still gets `401` with a `WWW-Authenticate` challenge — *I do not know who you are* — while a request from a caller the server has identified and will not allow gets `403`. Transport security does not settle the argument either: encrypting the connection protects the credential from someone on the wire, and every attack above happens inside the browser or inside another site's page, where the connection has already been terminated. ## What to say when you are asked Name the two losers, then refuse the "more secure" framing deliberately: *neither is safer until you say which attack you are buying down, and each one hands you a different obligation.* Then say which you would choose for the application in front of you, and why. That last sentence is the one actually being marked.
- Why is "keep the access token in a variable only" not a free win?It buys nothing against injection — script running in the same document reads a variable as easily as a storage slot. What it buys is that the credential does not survive the document: no reload, no second tab, nothing left behind. The cost is that the application needs a way to obtain a fresh token whenever the page reloads, which is machinery you now have to build and keep working.
- A team says they use a server-side session reference instead of a self-contained token, so custody does not apply to them. Are they right?No. A session reference is still a bearer credential: it rests somewhere and is carried somehow, so the same two losers apply unchanged. What genuinely differs is afterwards. A server holding the state can delete the record, so a stolen reference dies the moment you kill it, whereas a stolen self-contained token keeps verifying until its `exp` unless you carry extra bookkeeping to stop it.
- Does moving a credential from a cookie to an `Authorization` header change who can steal it?It changes who can *send* it. A header is not attached automatically, so a page on another origin cannot make the browser add it, and the forgery obligation largely goes away. But something in the page has to hold the value in order to set the header, which puts the credential back within reach of the page's own script. You have swapped one loser for the other, not escaped the choice.
Two ways to leave your front-door key while you are out. Leave it in an unlocked drawer in a room strangers walk through, and anyone who wanders in takes it and lets themselves in next month from anywhere. Or hand it to a concierge who will open the door for whoever asks in your name — nobody can take the key, but the concierge has no way to tell your instruction from a forged one until you give him a way. Neither arrangement is safe; they fail differently, and the second one leaves you owing the concierge a rule.
saying these in an interview costs you the question
- Claiming a cookie is simply more secure than script-readable storage
- Thinking cookie custody removes the forgery problem rather than creating the obligation
- Saying script-readable storage is fine because the connection is encrypted
- Treating a stolen access token as harmless because it expires soon
- Assuming an in-memory-only token is unreachable by injected script