Follow a Login
One login traced end to end at four zoom levels — browser to TLS to password verification to session to cookie to the authorized read — naming at every stage what is sent, what boundary is crossed, what must be protected, and what an attacker would try.
Frame the problem
Security starts with a concrete asset, attacker capability and trust crossing.
Level 1 and 2: the shape, then the mechanism
At the coarsest level a login is three boxes: a user submits a credential, the system verifies it, and the user is now authenticated. Every security question hides inside "verifies it" and "is now authenticated", and the value of zooming in is that each expansion introduces exactly one new asset to protect.
At the second level the mechanism appears. The browser sends the credential over TLS; the backend fetches the stored password hash for that account; a slow password-hashing function recomputes the hash from the submitted password and compares in constant time; on success the backend creates a session and returns it as a cookie. Four new assets: the credential in transit, the stored hash, the comparison itself, and the session.
Notice what has already gone wrong in many real systems by this point. The credential is logged by an access log that records POST bodies. The hash comparison uses == on strings, leaking timing. The session id is generated from a non-cryptographic random source. None of these are exotic; all are invisible at level 1.
Level 3: the authenticated request that follows
The login is only half the story; the interesting half is what the resulting cookie does on the next request. The browser attaches the cookie automatically to any request to the origin — *including requests initiated by other sites*, which is the entire basis of Cross-Site Request Forgery (CSRF). The backend looks the session id up in a session store, resolves it to a user, and only then can authorization begin.
This is where the two halves of access control meet. The session lookup answers "who"; it does not answer "may they". A system that stops here — session valid, therefore serve the resource — has the bug from Authentication vs Authorization, and it is at exactly this hop that it appears.
The session store choice has consequences that show up much later. A server-side store means revocation is a delete and takes effect immediately. A self-contained signed token means no lookup and no immediate revocation, which is the trade examined in JWT — What It Is and What It Costs.
POST /login HTTP/1.1 ← over TLS 1.3
Host: app.example
Content-Type: application/json
{"email":"alice@example.com","password":"••••••••"}
▲ never logged, never in a URL, never in a query string
HTTP/1.1 200 OK
Set-Cookie: sid=8Fq2...9xK; HttpOnly; Secure; SameSite=Lax; Path=/; Max-Age=43200
│ │ │ │
│ │ │ └─ not sent on cross-site POSTs (CSRF depth)
│ │ └───────── HTTPS only; never leaks over plaintext
│ └─────────────────── unreadable by JavaScript (XSS depth)
└──────────────────────────────────── 128+ bits from a CSPRNG, opaque, no user data
GET /invoices/101 HTTP/1.1
Host: app.example
Cookie: sid=8Fq2...9xK ← attached automatically, by any page on the origin
server: sid → session → principal(alice, tenant A) ← authentication
server: invoice 101 → owner tenant B ← authorization input
server: A ≠ B → 404 ← the decision that actually protects dataLevel 4: security internals, stage by stage
At full zoom every stage has an asset, an attacker and a control, and they are all different. This table is the lesson: the same login is defended by six unrelated mechanisms, and the weakest one sets the outcome.
Two stages deserve emphasis. The stored hash is the only thing standing between a database disclosure and every user's password — including on the other services where they reused it. That is why the choice of hashing function is disproportionately important relative to how little code it is. And the authorization stage is the only one where the attacker is a legitimate user, which is why it is the one that survives every improvement to the others.
| Stage | Asset | Attacker & method | Control |
|---|---|---|---|
| In transit | The password itself | Network observer, hostile Wi-Fi, downgrade attempt | TLS with certificate validation; HSTS so there is no plaintext first request |
| At the edge | The password again | Access logs, error reports, APM traces capturing bodies | Explicit redaction; never accept credentials in a URL or query string |
| Verification | The stored hash | Offline cracking after a database disclosure | A slow, salted, memory-hard password hash with a tuned work factor |
| Comparison | The comparison itself | Timing differences revealing prefix matches | Constant-time comparison; uniform response time for unknown accounts |
| Session creation | The session id | Guessing a weak id; session fixation | 128+ bits from a CSPRNG; issue a fresh id on login, never reuse a pre-login one |
| Cookie transport | The session id in the browser | XSS reading it; CSRF riding it; plaintext leaking it | HttpOnly, Secure, SameSite, narrow Path, short lifetime |
| Authorization | The resource | A legitimate logged-in user changing an id | Ownership check against the loaded resource |
| Afterwards | The audit trail | Denial that the action occurred | Audit event with actor, action, resource, time, source, result |
Key points
- Zooming in on a login introduces one new asset per level; six unrelated controls defend one flow.
- The credential must never reach a log, a URL, a query string or an error report — redaction is a design requirement, not a cleanup.
- Session id: 128+ bits from a CSPRNG, freshly issued at login, delivered with
HttpOnly; Secure; SameSite. - Session lookup answers "who", never "may they" — authorization is a separate decision after the resource loads.
- The stored hash is the last defence for every service where the user reused that password.
Follow a Login
Change the system and observe which assumption moves.
Follow the attack
Safe conceptual simulation: capability → missing control → crossed boundary → asset impact.
- 1Attacker → network: attempt to observe or downgrade the connection; defeated by TLS plus HSTS, so move on.
- 2Attacker → credential store: obtain the database through some other bug; value depends entirely on the hash function used.
- 3Attacker → session: steal the cookie via XSS, or ride it via CSRF, because the browser attaches it automatically.
- 4Attacker → authorization: give up on stealing an identity and simply request another user's resource with their own valid session.
- Credential compromise: this account plus every other service where the password was reused.
- Session compromise: full access as the user for the token's lifetime, with no credential to revoke unless sessions are revocable.
- Authorization failure: data belonging to users who did nothing wrong and cannot be notified as "compromised accounts".
Defend, detect, recover
One prevention is a single point of security failure. Layer it and make failure observable.
- • TLS everywhere with HSTS; no mixed content and no plaintext fallback for the login origin.
- • Redact credentials at the logging layer by field name, not by hoping nobody logs the body.
- • Use a modern password hash with a tuned work factor, and a constant-time comparison.
- • Issue a new session id on every privilege change; set all three cookie attributes; keep the lifetime short and refresh on activity.
- • Enforce resource-level authorization after loading, independent of the session check.
- • Alert on logins succeeding from a device and geography never seen for that account.
- • Alert on the same session id presented from two distinct network locations within a short window.
- • Alert on a spike in failed logins per source and per account, distinguishing stuffing from targeted attempts.
- • Revoke the session server-side; a cookie you cannot revoke is a design decision you are now paying for.
- • Force credential re-establishment and check for persistence created during the window.
- • Notify through a channel the attacker did not just change.
- • A phished credential presented by the real user through the real form is indistinguishable from a legitimate login without device or behavioural signals.
- • Malware on the user's device defeats every server-side control by acting inside the authenticated session.
- • Session lifetime is a permanent trade between the cost of re-authentication and the value of a stolen token.