Mesrai
Back to blog
// essaySecurity

Session Fixation in Login Flows: A Mesrai Diff

Real PR catch: login handler not regenerating session id — session fixation. Walkthrough with attack chain + canonical regenerate fix. CWE-384.

Mesrai TeamJuly 23, 20268 min read

An email/password login. After authentication, the user id is stored in the session. The line is missing one call — `regenerate` — that distinguishes a safe login from a session-fixation vulnerability.

The vulnerable diff

tsauth/login.ts
// auth/login.ts
authRouter.post("/login", async (req, res) => {
  const user = await authenticate(req.body.email, req.body.password);
  if (!user) return res.status(401).json({ error: "bad credentials" });

  // BUG: session id unchanged from pre-login state
  req.session.userId = user.id;
  res.json({ ok: true });
});

What is wrong

Session fixation works on a simple principle: if the attacker can plant a session id in the victim's browser before the victim logs in, and the server keeps that same session id after authentication, the attacker now shares the authenticated session. The fix is to discard the pre-auth session id and generate a fresh one at the moment of successful login. This is CWE-384 and is one of the OWASP Session Management Cheat Sheet's flagship rules.

The attack

The most common planting vector is a subdomain cookie attack — `evil.example.com` can set a cookie that the browser sends to `app.example.com` if the cookie domain is `.example.com`. Other vectors: physical access to a public terminal, URL parameter session ids on legacy apps, XSS that sets `document.cookie`.

textexploit
1. Attacker plants known session id on victim's browser
   (subdomain cookie / URL param / shared terminal).

2. Victim visits app.example.com, sees the login page, enters
   credentials. Server validates, sets userId on the session.

3. Server does NOT regenerate the session id. The attacker, who
   already knew the id from step 1, is now logged in as the victim.

4. Both browsers share the same authenticated session until logout.

Detection is hard — there is no failed login, no anomaly. The victim sees a normal session. The only signal is parallel activity from multiple IPs against the same session id, which not all teams alert on.

Mesrai's review comment

textreview-comment.txt
mesraipilot · Bot · reviewed 90 sec ago

[mesrai] [code-review] [Security] [CWE-384] [high]

This login flow does not regenerate the session id after successful
authentication. Session fixation is the resulting attack class.

Fix: discard the pre-auth session and generate a fresh one before
setting `userId`. With express-session:

  await new Promise<void>((resolve, reject) =>
    req.session.regenerate(err => err ? reject(err) : resolve())
  );
  req.session.userId = user.id;

For other session libraries the equivalent calls are `req.session_regenerate_id`
(PHP), `request.session.cycle_key()` (Django), `session.invalidate()`
followed by a new session (Spring).

Reference: OWASP Session Management Cheat Sheet § Session ID
Regeneration, CWE-384

The fix

tsauth/login.ts (fixed)
// auth/login.ts — fixed
authRouter.post("/login", async (req, res) => {
  const user = await authenticate(req.body.email, req.body.password);
  if (!user) return res.status(401).json({ error: "bad credentials" });

  // Regenerate session id — discards any pre-login attacker-planted id
  await new Promise<void>((resolve, reject) =>
    req.session.regenerate(err => err ? reject(err) : resolve())
  );
  req.session.userId = user.id;
  res.json({ ok: true });
});

The `regenerate` call wipes the existing session, creates a fresh id, and re-sets the cookie. The pre-auth id is now invalid; any attacker holding it is locked out. This is the canonical session-fixation defense for any session library that exposes a regenerate primitive.

Why human review missed it

Session fixation is one of those bugs that only makes sense if you have studied the attack class — the code reads as 'I set the user id on the session,' which sounds correct. Reviewers who have not internalized the regenerate-on-login rule trust the framework defaults. Mesrai's rule pack catches every login handler that writes user identity to a session without first regenerating the id.

Related rules + further reading

Mesrai rule pack: security/session-fixation — flags login handlers without session regeneration.

OWASP: Session Management Cheat Sheet § Session ID Regeneration.

CWE-384 — Session Fixation.

Takeaway

Regenerate on login. One line. Closes the entire fixation class.

// try

See it on your next PR.

Free for individuals. Install in two minutes. Mesrai reviews every commit.