Session fixation is when the victim logs in with an ID the attacker prepared. Here's how it works and how to reliably prevent it (no attack steps).
Why it works
The cause is a design where the session ID doesn't change across login. If it doesn't, an ID the attacker knew before login still works as "authenticated" after login.
It can pair with CSRF or XSS, but the essence of fixation itself is a single thing: the ID wasn't regenerated at login.
Japan's IPA (Information-technology Promotion Agency) guide "How to Secure Your Website" treats this as one form of improper session management and says every website with a login function needs to watch for it. Its fundamental fix is to start a new session after a successful login.
Fixation vs. hijacking vs. CSRF
These three are easy to mix up. All of them abuse a logged-in state, but they differ in what the attacker has and which defense works.
| Item | Session fixation | Session hijacking | CSRF |
|---|---|---|---|
| What the attacker has | An ID they prepared (known from the start) | A stolen, already-issued ID | No ID at all |
| What happens to the victim | They log in with the planted ID | Their issued ID is stolen | They're made to send an unintended request |
| Main defense | Regenerate the ID at login | HttpOnly/Secure, XSS prevention, short lifetimes | CSRF tokens, SameSite |
Fixation is the one that a single step — regenerating the ID at login — fixes at the root.
Defense: regenerate the ID at login
Regenerate the session ID on successful login (most important)
Don't accept session IDs via the URL
Set cookie flags
HttpOnly (JS can't read), Secure (HTTPS only), and SameSite (curbs cross-site sending). Weakens related hijacking paths together.Expire sessions sensibly
The OWASP Session Management Cheat Sheet recommends regenerating the ID on every privilege change and using a "strict" mechanism that only accepts IDs the server itself generated. A "permissive" mechanism that adopts an unknown ID brought in from outside leaves room for an attacker-chosen ID to be accepted.
What common frameworks do by default
Most frameworks regenerate the ID automatically if you use their standard login. The risk comes when you write the login yourself or turn the default protection off.
| Environment | Default behavior | What to check if you write it yourself |
|---|---|---|
| PHP (plain sessions) | Doesn't regenerate on its own; session.use_strict_mode defaults to 0 | Call session_regenerate_id(true) on successful login; set use_strict_mode to 1 |
| Laravel | Regenerated automatically on auth when you use a starter kit or Fortify | If you hand-write login, call $request->session()->regenerate() |
| Spring Security | On by default (changeSessionId on Servlet 3.1+) | Make sure nobody set sessionFixation().none() |
In PHP, the settings that pass session IDs in URLs (enabling session.use_trans_sid, disabling session.use_only_cookies) were deprecated in PHP 8.4. If you carry an old config file forward, review them.
How to check your own site
On a site you manage, check these four things. Developer tools are all you need.
- In developer tools under "Application" (Firefox: "Storage"), note the session cookie value, then look again after logging in. If it hasn't changed, the ID isn't regenerated at login
- Check that the login page and post-login URLs carry no session ID such as
PHPSESSID=or;jsessionid= - In the same panel, check the session cookie has
HttpOnly,SecureandSameSite - After logging out, put the old cookie value back and reload; confirm you are not logged in again
Cookie attributes are also covered by the site security audit, which checks sites whose ownership you have verified. Putting check 1 into an automated test whenever you touch the login flow means you notice immediately if the regeneration gets dropped.
This site's view: don't disable the framework's 'regenerate'
Session fixation is mostly prevented by a single step: regenerating the ID at login, not by elaborate custom code. The key is to not accidentally disable or forget to call the mechanism your framework already provides. Using a proven implementation's safe defaults beats hand-rolling your own session management. On this site we hold to "don't build auth/key handling yourself; don't disable the safe defaults." As with IDOR, authentication/authorization should be enforced by a mechanism on every request.
Sources (primary)
- IPA, "How to Secure Your Website", 1.4 Improper session management — ipa.go.jp (start a new session after login; don't put IDs in URLs; Japanese)
- OWASP Session Management Cheat Sheet — cheatsheetseries.owasp.org (regeneration on privilege change, strict mechanism, cookie attributes)
- MITRE CWE-384: Session Fixation — cwe.mitre.org
- PHP Manual, "Runtime Configuration" (sessions) — php.net (defaults such as
use_strict_mode; 8.4 deprecations) - Laravel docs, "HTTP Session" — laravel.com / Spring Security, "Authentication Persistence and Session Management" — docs.spring.io
Read next
- Glossary: What is CSRF · What is XSS · What is IDOR · What is a JWT
- Basics: Authentication vs. authorization