Prevent Session Fixation with session_regenerate_id in PHP

This article explains the security vulnerability known as session fixation, details how malicious actors exploit it to hijack user sessions, and provides a straightforward guide on how to secure your PHP applications against this threat using the built-in session_regenerate_id() function.

What is Session Fixation?

Session fixation is a web security vulnerability where an attacker steals a user’s session by establishing a known session ID before the user even logs in. Instead of stealing a session ID after login, the attacker “fixes” the session ID beforehand and forces the victim’s browser to use it.

The typical attack workflow happens in three steps: 1. The Attacker Obtains a Session ID: The attacker visits the target website, receives a valid session ID (e.g., PHPSESSID=12345), and keeps it active. 2. The Attacker Tricks the Victim: The attacker sends a link to the victim containing the fixed session ID (for example, http://example.com/?PHPSESSID=12345) or injects the session cookie into the victim’s browser. 3. The Victim Logs In: The victim clicks the link and logs into their account. Because the application does not change the session ID upon authentication, the victim’s authenticated state is now tied to the ID 12345. 4. The Attacker Gains Access: The attacker, who already knows the session ID is 12345, accesses the site using that ID and gains full access to the victim’s authenticated account.

Preventing Session Fixation in PHP

The most effective way to prevent session fixation is to change the session ID immediately after a user’s privilege level changes, such as during a successful login.

PHP provides a built-in function specifically for this purpose: session_regenerate_id(). This function replaces the current session ID with a new, randomly generated one while keeping the existing session data intact.

How to Use session_regenerate_id()

To secure your authentication flow, you must call session_regenerate_id(true) immediately after verifying the user’s credentials and before setting any session variables that indicate they are logged in.

Here is a practical example of how to implement this in a PHP login script:

<?php
session_start();

// 1. Retrieve and validate user credentials
$username = $_POST['username'] ?? '';
$password = $_POST['password'] ?? '';

if (validate_credentials($username, $password)) {
    
    // 2. Prevent Session Fixation by regenerating the ID
    // Passing 'true' deletes the old session file on the server
    session_regenerate_id(true);
    
    // 3. Mark the user as authenticated
    $_SESSION['authenticated'] = true;
    $_SESSION['username'] = $username;
    
    // Redirect to the dashboard
    header("Location: dashboard.php");
    exit;
} else {
    // Handle login failure
    echo "Invalid username or password.";
}
?>

Why Pass “true” to session_regenerate_id()?

The session_regenerate_id() function accepts a boolean parameter called $delete_old_session.

You should always pass true during a login event. Deleting the old session file ensures that the attacker’s fixed session ID becomes instantly invalid and completely useless.