A session ID is like a key. In session fixation, the attacker does not steal the key. They give the victim a key that the attacker already knows, and wait for the victim to log in.
The attack
- The attacker visits your site and gets a session ID, say
abc123. - They trick a victim into using that ID, for example with a link
https://shop.example/?PHPSESSID=abc123(if the site accepts IDs in the URL), or by planting a cookie through another weakness. - The victim logs in. The site marks session
abc123as logged in. - The attacker, who also uses
abc123, is now logged in as the victim.
The attacker never needed the password.
The fix: a new ID at login
Create a new session ID at the moment of login (and whenever privileges change). The old abc123 becomes worthless.
<?php
session_start();
if (passwordIsCorrect($email, $password)) {
session_regenerate_id(true); // true: delete the old session file
$_SESSION["user_id"] = $userId;
}
The attacker’s known ID no longer leads to a logged-in session.
More protection
<?php
ini_set("session.use_strict_mode", "1"); // refuse IDs the server did not create
ini_set("session.use_only_cookies", "1"); // never accept an ID from the URL
ini_set("session.cookie_httponly", "1");
ini_set("session.cookie_samesite", "Lax");
ini_set("session.cookie_secure", "1"); // only over HTTPS
- Strict mode stops the server from adopting an unknown ID that a client suggests.
- Cookies only removes the
?PHPSESSID=trick. - HttpOnly, SameSite and Secure reduce the ways an ID can be read or planted.
Regenerate when privileges change
Call session_regenerate_id(true) also after:
- Login.
- Logout (you destroy the session anyway).
- A password change.
- Changing the user’s role or entering an “admin mode”.
Why true?
<?php
session_regenerate_id(); // the old session data stays on the server
session_regenerate_id(true); // the old session data is deleted
Deleting the old session is safer. One caveat: on a very unreliable network, a second request that is still using the old ID may arrive just after the switch. Some frameworks keep the old session for a few seconds. For most sites, true is the right choice.
Test it yourself
- Open the login page and note the
PHPSESSIDcookie in the browser developer tools. - Log in.
- Look at the cookie again. The value should be different. If it is the same, the site is vulnerable.
A summary table
| Weakness | Fix |
|---|---|
| ID unchanged after login | session_regenerate_id(true) |
| Session IDs accepted in the URL | session.use_only_cookies=1 |
| Server accepts any ID | session.use_strict_mode=1 |
| ID readable by JavaScript | HttpOnly cookie |
| ID sent over HTTP | Secure cookie and HTTPS |
See Secure Session Practices for the complete setup.
A one-line habit
$_SESSION["user_id"] = ..., the line above it should be session_regenerate_id(true);.