Your server drops a session when the request's User-Agent differs from the one recorded at login — why do real users hit this?
answer
- the client picks this value
- changes with no user action
- an update bumps the version
- a thief replays the same string
basics
~20 sThe User-Agent string is chosen and sent by the client, and it changes on its own — an automatic update rewrites it mid-session. Binding a server-side session record to it logs out honest users while a thief simply replays the string.
solid answer
~50 s`User-Agent` is a client-asserted header: the server never verifies it, and the client rewrites it without the human doing anything. An automatic update bumps the version, a mobile client switches to a desktop rendering mode, a web view embedded in another application sends a different string than the standalone client, and a privacy or fleet policy can reduce it to a generic value. Any of those changes the exact string while the same person keeps working, so an exact-match binding turns a routine update into a mass logout. It also buys very little: whoever copied the session cookie copied the request that carried it, so replaying the recorded string is free. If you use the signal at all, compare a normalised family and major version, and treat a mismatch as one input to a score rather than as a verdict.
code
json · 9 lines{
"sessionId": "<opaque reference held in the cookie>",
"boundAtLogin": { "agentFamily": "desktop-client", "agentMajor": 141 },
"observedNow": { "agentFamily": "desktop-client", "agentMajor": 142 },
"comparison": "family matches, major version advanced by one",
"verdict": "record-only",
"reason": "a forward major-version bump is an update, not a new caller",
"action": "re-baseline boundAtLogin to observedNow"
}go deeper
Recall who writes this header: the client does, and the server only reads it. That one fact explains both why the binding is weak evidence and why honest users trip it after an update.
Explain the concrete causes of drift — automatic updates, a desktop-rendering toggle, an embedded web view — and why normalising to family and major version is the only comparison worth storing on the session record.
Show that you measured the forced-logout rate before enabling any such check, and that a mismatch feeds a score routed to a challenge or a log line rather than ending the session record outright.
Argue the trade openly: the signal has almost no true positives because the string travels with the stolen cookie, so the question is whether any false-positive budget at all is justified for it.
## What the header actually is `User-Agent` is a request header (RFC 9110 defines it) carrying the client's **self-description**: a product name, a version, and usually a pile of legacy tokens kept for compatibility. Nothing about it is verified by anyone. The server reads what the client chose to send, and a client is free to send anything at all. That single property decides everything else: a value the caller controls is evidence about the caller only to the extent the caller has no reason to lie. **Binding** here means something specific. At login the server stored the observed value on the **server-side session record** — the record the opaque identifier in the cookie names — and on every later request it compares the incoming header against the stored copy, treating a mismatch as a sign that the identifier has changed hands. It is not a cookie attribute and the client enforces none of it; the comparison is entirely yours. ## Why the value moves with no user action at all | Cause of change | Typical cadence | Who it hits | |---|---|---| | Automatic client update | weekly to monthly | every user of that client version | | Desktop-rendering toggle on a mobile client | user-initiated, mid-session | one user, repeatedly | | Web view embedded in another application | on every context switch | anyone opening your page from inside another app | | Privacy setting reducing the string | on policy rollout | a self-selecting minority | | Managed fleet rollout | scheduled, often overnight | every terminal in one deployment, simultaneously | Three of those five change the string for a **population** rather than an individual, and that is what makes exact matching dangerous. The failure mode is not "one user is annoyed". It is "every session on the ward ended inside the same five minutes, during handover, because operations pushed a client update". Correlated false positives are the ones that get a security control switched off permanently. ## What the binding is worth against an actual thief - The string **travels in the same request as the cookie**. Whoever captured the session identifier off the wire, out of a log, or through an injected script captured the header beside it, so replaying it costs nothing. - The value is **low-entropy and widely shared**: thousands of callers send byte-identical strings, so a match is not even weak individuating evidence. - An attacker who somehow lacks the string can **enumerate it**: there are only so many plausible values, and a wrong guess costs one rejected request. Put those together and the honest accounting is: a true-positive rate near zero, against a false-positive rate that is small per user but arrives all at once for a whole population. The check does not prevent reuse of a stolen reference; at best it raises the cost of using one by an amount close to nothing. ## Using the signal honestly, if you use it 1. **Normalise before you store.** Keep the agent family and the major version on the session record and discard the rest of the string. 2. **Compare the normalised form**, so a patch-level bump is invisible and a genuine change of client family is not. 3. **Score, do not gate.** A mismatch is one input alongside network movement and request shape; on its own it is not enough to act on. 4. **Re-baseline after acceptance.** Once a change is accepted, overwrite the stored copy, or one update is scored against the user forever. 5. **Route the verdict**, and remember the routing choice is allow-and-record, challenge, or end — a lone agent-string mismatch earns the first of those. ## What this looks like on a shared, fixed terminal On a cabled terminal used by three clinicians across a shift, the agent string never varies between humans — which sounds like the ideal case for binding and is in fact the opposite. A signal that never varies produces no false positives **and no true positives**: the realistic risk there is the next clinician continuing under the previous one's open session, and the header is byte-identical in that case. Stability is not evidence. The check is silent, not effective, and the thing it would need to distinguish is invisible to it. The defensible summary for an interview: `User-Agent` is a self-description, not an identity; bind coarsely or not at all; and if a mismatch is going to end someone's session, be able to state how many sessions that ends per month and who absorbs it.
- How would you make an agent-string comparison useful instead of dropping the idea entirely?Normalise before comparing: store the agent family and major version on the session record, ignore the rest, and re-baseline once a change is accepted so one update is not scored forever. Feed the mismatch into a score alongside network movement and request shape, and let a lone mismatch route to record-only.
- Three nurses share one cabled terminal whose agent string never changes across a shift — does that stability make the binding safe there?It makes it silent, not safe. A signal that never varies yields no false positives and no true positives. The real risk on that terminal is the next clinician working under the previous one's still-open session, and the header is identical in that case. Stability is not evidence of a single caller.
A visitor badge the visitor prints for themselves: comparing it to the copy on file proves only that they still own the printer, and it stops matching the day the template changes.
saying these in an interview costs you the question
- Treats the agent string as something the client cannot change
- Says a matching agent string proves the same person is calling
- Assumes agent strings stay fixed for the life of a session
- Claims exact-match agent binding stops replay of a copied session cookie
- Ends the session record immediately whenever the string changes