Session Fixation, Drawn (Diagram)

Nobody guessed a password and nobody stole a cookie. The attacker asked your app for a session ID while signed out, arranged for your browser to carry the same one, and then waited for you to log in and make it valuable.

A sequence diagram of session fixation. The attacker asks your app for a session ID while signed out, mails you a link carrying that ID, and when you sign in your app keeps the same ID instead of issuing a new one, so the attacker's copy is now signed in as you.
The attacker never guesses your session ID. Your app gave it to them first.Full explanationMermaid source

Reading the diagram

Steps 1 and 2. The attacker visits your site like anyone else and takes the session ID your app hands out to anonymous visitors. Nothing is broken yet. abc123 is worthless: it belongs to nobody and can see nothing.

The note in the middle. This is the delivery hop, and it happens outside your app entirely. A link with the session ID in a query parameter, a subdomain the attacker controls setting the cookie, a cross-site scripting bug on a neighbouring page. The mechanics vary. What matters is that your browser and the attacker's browser are now carrying the same session ID.

Steps 3 and 4, the red band. You sign in. Your password is correct, your two-factor code is correct, everything about this login is legitimate. Your app looks up abc123, marks it as belonging to you, and sends you on your way with the same ID it had a second ago.

That single decision is the vulnerability. Not a weak password, not a missing check on a route. The app reused a value that a stranger already knew.

Steps 5 and 6. The attacker refreshes. Their copy of abc123 is the same string it always was, and your app now considers that string signed in as you.

The dead space on the attacker's side. Look at the gap under "The attacker" while you sign in. That emptiness is the point of the diagram: the attacker does nothing during the part where the security actually happens. They do not need to. Your login did the work.

The fix is one line, in one place

Issue a new session ID when authentication succeeds, and delete the old one.

Regenerate at the privilege boundary
// after the password and any second factor have been verified
await destroySession(oldSessionId);        // the attacker's copy now points at nothing
const newSessionId = crypto.randomBytes(32).toString('hex');
await createSession(newSessionId, user.id);
setSessionCookie(res, newSessionId);

The attacker's ID is not blocked, blacklisted, or detected. It simply stops referring to anything, which is stronger than any of those.

Login is not the only boundary

Regenerate on password change and on any role or permission change too. If someone was signed in as a normal user and you promote them to admin without a new ID, any pre-existing copy of that ID is now an admin session.

Why AI-generated auth misses this

Session fixation is invisible in a working app. Log in, everything works. Log out, everything works. There is no error, no failing test, and no user-facing symptom until someone deliberately arranges the setup above.

Generated auth code optimises for the flow that gets exercised: create a session, read it back, delete it on logout. Regeneration sits in none of those paths, so it drops out. We see this pattern in scans of AI-built apps regularly, alongside its sibling: the logout that clears the cookie in the browser without deleting the row on the server.

Thirty-second check, no tooling: open devtools, look at your session cookie signed out, sign in, look again. Same value means you are vulnerable.

What is session fixation?

An attack where the attacker chooses your session ID before you log in, rather than stealing it afterwards. They take an anonymous session ID from the app, plant it in your browser, and wait. If the app carries that ID through login, their copy becomes an authenticated session for your account.

How is session fixation different from session hijacking?

Hijacking is theft after the fact, so the attacker has to get hold of a live session cookie somehow. Fixation is arranged in advance and needs no theft, because the attacker picked the ID. That difference is why an httpOnly cookie defeats one and not the other.

How do I fix session fixation?

Issue a brand new session ID the moment authentication succeeds and delete the old one. That single change closes the whole attack, because the ID the attacker holds stops pointing at anything. Do the same on password change and on any role change.

Does httpOnly or SameSite stop session fixation?

No. Both exist to stop an attacker reading or sending your cookie, and fixation needs neither. Keep them, they are doing other useful work, but regeneration at the privilege boundary is the control for this one.

How do I test whether my app is vulnerable?

Open your app signed out, copy the session cookie value from devtools, sign in, and look again. Identical value means no regeneration happened and fixation works. It is about a thirty-second check and it needs no tools beyond the browser.

The source

---
title: "The planted session ID survives login"
---
sequenceDiagram
    autonumber
    participant A as The attacker
    participant S as Your app
    participant B as Your browser

    A->>S: opens your site, signed out
    S-->>A: here is a session ID: abc123

    Note over A,B: then mails you a link with abc123 in it

    rect rgb(254, 242, 242)
        B->>S: you sign in, still carrying abc123
        S-->>B: welcome back. same session ID.
    end

    A->>S: reloads, still holding abc123
    S-->>A: your account, your data

    Note over A,B: nothing was stolen. your app reused the ID<br/>instead of issuing a new one at sign-in.

Does Your Login Issue a New Session?

CheckYourVibe reads what your deployed app actually sets, including a session cookie that never changes across the login boundary.

diagrams

Session Fixation, Drawn (Diagram)