Session fixation was described in detail in 2002. Frameworks have defended against it by default since roughly 2008. And yet, every year, it shows up in new code bases, usually written from scratch for “security reasons.”
The attack
Alice visits example.com. A subdomain XSS, or a crafted URL, or a malicious Wi-Fi proxy plants a session cookie on her browser with a session ID that the attacker already knows. Alice then signs in — and because the session ID didn’t change on login, the attacker now shares her authenticated session.
Or the variant: the attacker shares their own authenticated session ID with the victim (via a link, iframe, etc.). The victim browses in the attacker’s session, and any actions taken (changing profile, entering payment info) happen on the attacker’s account, which the attacker observes.
The fix, in one sentence
Rotate the session ID on every privilege change.
More specifically, generate a new session ID when:
- The user signs in
- The user signs up
- The user completes MFA
- An admin impersonates another user
- The user resets their password
Invalidate the old session ID. Store the new one in the cookie. Done.
In frameworks that do this automatically (Rails, Django, Laravel, ASP.NET), you’re already safe. In frameworks that don’t (many hand-rolled Express/Koa setups, some Go HTTP servers), it’s a two-line change that you need to make once and test once.
Why it’s still a bug
The reason session fixation survives in 2026 is that most custom session implementations follow a pattern like:
// sign-in handler
const user = await authenticate(email, password)
session.user = user // attach user to existing session
That session.user = user line is where the bug lives. The session cookie hasn’t changed — only its server-side state has. The attacker who planted the session cookie still has a valid session, just now bound to Alice’s account.
The fix is equally simple:
const user = await authenticate(email, password)
await session.regenerate()
session.user = user
regenerate() (or whatever your framework calls it) deletes the old session row and creates a new one with a fresh ID. The old cookie no longer maps to anything.
The test
Log in, grab the session cookie. Now log out and log in again. Did the cookie value change? If yes, you’re probably fine. If no, you have session fixation.
Write this as a unit test and run it in CI. Future refactors won’t break your defense.