# Session fixation prevented: new session ID on login

- **Pattern:** `ab-001472` (`finserv-session-security.session-integrity.session-fixation-prevention`)
- **Severity:** critical
- **Lifecycle:** active
- **Last modified:** 2026-04-18
- **Canonical URL:** https://auditbuffet.com/patterns/ab-001472
- **License:** CC-BY-4.0 — attribute to AuditBuffet Pattern Catalog (https://auditbuffet.com/patterns/ab-001472)

## Why it matters

Session fixation (CWE-384) is an attack where an adversary pre-sets a known session ID — via a URL parameter, cookie injection, or a shared link — then waits for the victim to authenticate. If the server preserves the pre-authentication session ID after login, the attacker's known ID becomes an authenticated session without any credential theft. OWASP A07 and CAPEC-61 document this attack class. NIST IA-2 requires unique session identification per authenticated principal. In a financial application, a fixation attack grants the attacker a fully authenticated session capable of initiating transfers. The fix is a single call to `session.regenerate()` on every login path — omitting it is the entire vulnerability.

## Severity rationale

Critical because session fixation allows an attacker to pre-establish a known session ID and convert it into an authenticated financial session without ever stealing credentials — purely by exploiting a missing regeneration call on login.

## Remediation

Call `session.regenerate()` immediately after credential verification in `src/app/api/auth/login/route.ts` — the new session ID must be cryptographically random and independent of the pre-login ID:

```js
export async function POST(req: Request) {
  const { email, password } = await req.json();
  const user = await authenticateUser(email, password);
  if (!user) {
    return Response.json({ error: 'Invalid credentials' }, { status: 401 });
  }

  // CRITICAL: discard the pre-login session, issue a new ID
  await new Promise<void>((resolve, reject) =>
    req.session.regenerate((err) => err ? reject(err) : resolve())
  );
  req.session.userId = user.id;
  await new Promise<void>((resolve, reject) =>
    req.session.save((err) => err ? reject(err) : resolve())
  );

  return Response.json({ ok: true });
}
```

Verify that `regenerate()` is called in every login code path, including OAuth callbacks and SSO endpoints — not just the primary email/password route.

## Detection

- **ID:** `session-fixation-prevention`
- **Severity:** `critical`
- **What to look for:** Count all login endpoints and verify each calls session.regenerate() or equivalent. Quote the actual session regeneration function found. Check whether the session ID changes between pre-login and post-login states. A login endpoint that does not regenerate the session ID must not pass — do not pass if any login path reuses a pre-login session ID.
- **Pass criteria:** On successful login, at least 1 session.regenerate() call (or equivalent) exists in at least 100% of login endpoints. New session ID is cryptographically random. Report the count even on pass (e.g., "1 of 1 login endpoints calls session.regenerate(), pre-login session discarded").
- **Fail criteria:** Same session ID used before and after login in any login endpoint, or session.regenerate() not called (0 regeneration calls found).
- **Skip (N/A) when:** The application uses stateless JWT where each token is independently verified — cite the actual auth mechanism found.
- **Detail on fail:** `"0 session.regenerate() calls in login endpoint — pre-login session ID persists after authentication, enabling session fixation"`
- **Cross-reference:** Check `finserv-session-security.session-integrity.token-security-httponly` for token cookie security, and `finserv-session-security.session-lifecycle.logout-clears-data` for session cleanup.
- **Remediation:** Regenerate the session ID on login (in `src/app/api/auth/login/route.ts`):
  ```js
  // routes/api/auth/login.ts
  export async function POST(req: Request) {
    const { email, password } = await req.json();
    const user = await authenticateUser(email, password);
    if (!user) return Response.json({ error: 'Invalid credentials' }, { status: 401 });

    // CRITICAL: Regenerate session ID
    req.session.regenerate(() => {
      req.session.userId = user.id;
      req.session.save();
    });

    return Response.json({ ok: true, userId: user.id });
  }
  ```

## External references

- cwe CWE-384
- owasp A07
- nist IA-2
- capec CAPEC-61

Taxons: access-control

HTML version: https://auditbuffet.com/patterns/ab-001472
