Sessionssession hijackingtoken theftbearer tokenfixationbindingrevocation

Session Hijacking

The attacker does not need the password: possession of the session token is possession of the account, and every defense is either about preventing the token from escaping or about limiting what it is worth once it has.

▶ Run the labFollow the failure

Frame the problem

Security starts with a concrete asset, attacker capability and trust crossing.

Asset
The session token in the browser, in transit, and in every place it is inadvertently recorded.
Attacker & capability
Anyone with one of a handful of positions: script execution on your origin, access to a log, a plaintext network hop, a shared device, or a link the user pasted somewhere.
Trust boundary
The boundary between the user's browser and everywhere else — a boundary that leaks through logs, referrers, third-party scripts and shared screens.
AssetThreatAttack SurfaceTrust BoundaryVulnerabilityExploit PathImpactMitigationDefense in DepthResidual Risk

Every path the token takes to escape

A session token is a bearer credential: the server validates the token, not the bearer. That single property means the entire security of the session reduces to "did the token stay where it belongs", and there are more ways for it to leave than teams usually enumerate.

The paths are worth listing explicitly, because each has a different owner and a different fix. Some are code (XSS, tokens in URLs), some are configuration (missing Secure, missing HSTS), some are operational (logs, error reporting, session replay tools), and at least one is entirely outside your control (malware on the device).

How tokens escape, and what actually stops each
PathHow it happensControl
XSS reads itInjected script reads localStorage or a non-HttpOnly cookieHttpOnly cookies; CSP; fix the XSS
Plaintext transportOne http:// request carries the cookie in the clearSecure attribute + HSTS preload
URL leakageToken in a query string reaches logs, history, RefererNever put tokens in URLs
Log captureAccess logs, APM traces, error reports record headers or bodiesRedact by field name at the logging layer
Session replay toolingThird-party analytics records the DOM and network trafficExclude auth surfaces; review vendor scope
ReferrerOutbound link leaks a token-bearing URL to a third partyReferrer-Policy: strict-origin-when-cross-origin
Shared deviceSession persists after the user walks awayIdle timeout; explicit logout; short absolute lifetime
FixationAttacker plants a known id before login and the app reuses itRotate the session id at login
Device malwareReads the browser profile directlyOutside your control; limit with short lifetimes and step-up

Binding: useful, and less than it sounds

Since the server cannot tell the bearer apart from the user, a natural idea is to bind the session to something about the client so that a stolen token fails elsewhere. This works, partially, and the partiality matters.

IP binding breaks legitimate users constantly: mobile networks change addresses, corporate egress rotates, users move between Wi-Fi and cellular mid-session. Binding to a coarse network property (an autonomous-system number, or a country) as a *signal* rather than a hard requirement is defensible; binding to an exact address produces a support queue and gets removed.

Device or user-agent binding is cheap and catches unsophisticated theft, but the user agent is attacker-controlled — it travels in the request the attacker copied. Treat a mismatch as a strong signal for step-up, not as proof.

The genuinely strong version is cryptographic token binding, where the client proves possession of a private key on each request rather than merely presenting a bearer string. This removes the bearer property entirely: a copied token is useless without the key. It is what mutual TLS achieves between services and what DPoP-style proof-of-possession schemes achieve for tokens. Adoption in browsers remains limited, so for most applications the practical stance is: reduce escape paths, shorten lifetimes, require step-up for dangerous actions, and treat binding signals as inputs to risk scoring rather than as gates.

Limiting what a stolen session can do

Since prevention is imperfect, the second question is what an attacker gets. A session with unlimited authority over the account gives full takeover; a session that cannot change the email address, cannot disable MFA, and cannot create an API token gives temporary access that ends when the session does.

Step-up authentication on account-takeover primitives is the highest-value control here and is missing from most applications. It costs one re-authentication prompt on a handful of rare operations and removes the attacker's ability to convert a temporary foothold into permanent control.

Short absolute lifetimes cap the window. Immediate revocation ends it on demand. Visible session lists let users notice and act themselves — a surprisingly effective detection channel, because the user knows which devices are theirs and your anomaly detection does not.

And notification closes the loop: emailing the previously-known address on new-device login, email change, MFA change and token creation gives the legitimate user a chance to intervene, provided the notification cannot be suppressed by the attacker who just changed the address. Always notify the *old* address on a change of address.

  • Step up for: change email, change password, disable MFA, create API token, add OAuth app, change payout details.
  • Absolute lifetime caps the exposure; idle timeout limits abandoned-device risk.
  • Show users their sessions with device, location and last-seen, and let them revoke individually or all at once.
  • Notify the previously-known contact on every security-relevant change, and make the notification unsuppressable.
  • Rotate the session id at login and at every privilege change, without exception.

Key points

  • Session tokens are bearer credentials: the server validates the token, not the person, so theft is complete impersonation.
  • Enumerate every escape path — XSS, plaintext, URLs, logs, replay tools, referrers, shared devices, fixation — and own each one.
  • IP and user-agent binding are risk signals, not gates; strict binding breaks legitimate users and gets removed.
  • Step-up authentication on takeover primitives is the cheapest way to bound what a stolen session achieves.
  • Give users visible, revocable session lists — they detect theft better than your anomaly rules do.

Boundary control exercise

This lesson uses the shared boundary-control exercise.

Boundary control check
Untrusted input / identity
Trust boundary
Privileged asset
Prevention may fail silently.

Follow the attack

Safe conceptual simulation: capability → missing control → crossed boundary → asset impact.

  1. 1
    Attacker → position: obtain script execution, a log, a plaintext hop, or physical access.
  2. 2
    Position → token: read the cookie, the storage entry, the URL, or the log line.
  3. 3
    Token → replay: present it from their own client; the server sees a valid session.
  4. 4
    Session → permanence: change the recovery email, add an API token, or enrol a new MFA factor before the session expires.
Blast radius
  • Full impersonation with no credential prompt and no login event to alert on.
  • Actions attributed to the legitimate user throughout the audit trail.
  • If takeover primitives are reachable, temporary theft becomes permanent account control.

Defend, detect, recover

One prevention is a single point of security failure. Layer it and make failure observable.

Prevent
  • • Eliminate escape paths first: `HttpOnly` and `Secure` cookies, HSTS, no tokens in URLs, redaction in logging, scoped analytics.
  • • Rotate at login and privilege change; enforce idle and absolute timeouts server-side.
  • • Require step-up for all account-takeover primitives.
  • • Use CSP to reduce the chance that an injected script can exfiltrate anything even if it runs.
Detect
  • • Alert on one session used from two distinct locations or device fingerprints in a short window.
  • • Alert on mid-session changes to user agent or device fingerprint.
  • • Alert on takeover primitives invoked from a session that has not re-authenticated.
Respond & recover
  • • Revoke all sessions for the user, not just the suspicious one.
  • • Audit for persistence created during the window and remove it.
  • • Notify out of band and require re-authentication with a second factor.
Residual risk
  • • Malware on the user's device defeats every server-side control.
  • • A stolen token used from the same network and device as the user is not distinguishable by any signal you have.
  • • Third-party scripts on your origin operate inside the session by design.

Misconceptions

Claim
“Binding the session to the IP address prevents hijacking.”
Reality
It breaks mobile and corporate users constantly and is bypassed by an attacker on the same network or behind the same egress. Useful as a signal, harmful as a gate.
Claim
“If the session is stolen we can just change the password.”
Reality
Only if changing the password revokes other sessions, and only if the attacker has not already changed the recovery email or created an API token.