Session fixation (CWE-384, CAPEC-61) lets an attacker plant a known session token in a victim's browser before login, then use that same token after the victim authenticates. The attacker never needs to steal anything — they supplied the token themselves. This attack predates modern auth libraries and is largely invisible in code review because the vulnerability is an absence: the session is never regenerated after login. OWASP A07 (Identification and Authentication Failures) lists session fixation as a named failure mode, and the OWASP ASVS Session Management chapter requires that session identifiers be regenerated on authentication.
High because a successful session fixation attack gives the attacker an authenticated session for any user they can trick into logging in, with no stolen credentials required.
After successful credential verification, always replace the pre-login session with a freshly generated one before setting any user data on it:
// Express/Fastify — regenerate before setting user data
app.post('/login', async (req, res) => {
const user = await verifyCredentials(req.body)
if (!user) return res.status(401).json({ error: 'Invalid credentials' })
req.session.regenerate((err) => {
if (err) return res.status(500).json({ error: 'Session error' })
req.session.userId = user.id
req.session.save(() => res.json({ ok: true }))
})
})
Managed libraries (NextAuth, Clerk, Supabase Auth) regenerate sessions after login by design. If you have overridden the session token generation logic in any of these libraries, revert to the default.
ID: saas-authentication.session-management.session-fixation
Severity: high
What to look for: Examine the login flow. After successful authentication, a new session token should be generated — never reusing the pre-login session ID. Look for session regeneration calls after login (e.g., req.session.regenerate() in Express/Fastify). For cookie-based auth: check that the session cookie set after login is freshly generated, not derived from any pre-auth value. Managed auth providers (NextAuth, Clerk, Supabase Auth) handle this correctly by design. Count all instances found and enumerate each.
Pass criteria: A fresh, cryptographically random session token is generated upon successful login. The pre-login session (if any) is discarded and a new one is issued. Libraries that implement this by default count as passing. At least 1 implementation must be confirmed.
Fail criteria: Custom session implementation reuses a session ID that existed before login, or the session token is derived from a value the attacker could predict or set.
Skip (N/A) when: Fully managed auth provider is used with default configuration (NextAuth, Clerk, Supabase Auth, Auth0, Firebase Auth) — session fixation is handled by the library. Signal: auth library in package.json with no custom session token generation code.
Detail on fail: "Custom session middleware at src/lib/session.ts reuses the existing session ID after login rather than regenerating it" or "Login handler at /api/auth/login updates session data but calls req.session.save() without req.session.regenerate()".
Remediation: Session fixation attacks work by planting a known session ID before login and then using it to access the authenticated session. Fix it by regenerating the session after authentication:
// Express/Fastify session example
app.post('/login', async (req, res) => {
const user = await verifyCredentials(req.body)
if (!user) return res.status(401).json({ error: 'Invalid credentials' })
// Regenerate session to prevent fixation
req.session.regenerate((err) => {
if (err) return res.status(500).json({ error: 'Session error' })
req.session.userId = user.id
req.session.save(() => res.json({ ok: true }))
})
})
If using a managed auth library, verify you're using the default session handling and have not overridden the session token generation logic.