How to Prevent Session Hijacking in PHP

Securing user sessions is a critical step in protecting PHP applications from unauthorized access and data breaches. This article provides a direct, actionable guide on how to prevent session hijacking by implementing secure session cookie attributes, regenerating session identifiers, enforcing HTTPS, and validating client-side fingerprints.

Before starting a session in PHP, you must configure the session cookie parameters to ensure they cannot be accessed by malicious scripts or transmitted over unencrypted connections. Use session_set_cookie_params() or configure your php.ini file with the following settings:

session_set_cookie_params([
    'lifetime' => 0,
    'path' => '/',
    'domain' => 'yourdomain.com',
    'secure' => true,
    'httponly' => true,
    'samesite' => 'Strict'
]);
session_start();

2. Regenerate Session IDs Frequently

Session fixation occurs when an attacker establishes a session ID and forces the victim’s browser to use it. To prevent this, always regenerate the session ID immediately upon a change in authentication status (such as logging in or logging out) using session_regenerate_id(true). The true parameter deletes the old session file associated with the previous ID.

// Call this immediately after verifying user credentials
session_regenerate_id(true);
$_SESSION['user_id'] = $user['id'];

For high-security applications, consider regenerating the session ID periodically (e.g., every 15 minutes) during an active session.

3. Enforce Strict HTTPS

Session hijacking often occurs via packet sniffing on unencrypted networks. You must enforce SSL/TLS (HTTPS) across your entire application. This ensures that session IDs are encrypted in transit. Combine HTTPS with HTTP Strict Transport Security (HSTS) headers to force browsers to always use secure connections.

4. Bind Sessions to User Fingerprints

While IP addresses can change (especially on mobile networks), binding the session to a combination of the user’s IP address and User-Agent string adds an extra layer of defense. If a request arrives with a matching session ID but a different User-Agent, destroy the session immediately.

session_start();

$currentUserAgent = $_SERVER['HTTP_USER_AGENT'] ?? '';

if (!isset($_SESSION['user_agent'])) {
    $_SESSION['user_agent'] = $currentUserAgent;
} elseif ($_SESSION['user_agent'] !== $currentUserAgent) {
    // Potential session hijacking attempt
    session_unset();
    session_destroy();
    header("Location: login.php");
    exit();
}

5. Implement Strict Session Timeouts

Do not allow sessions to remain active indefinitely. Implement both idle timeouts (destroying the session after a period of inactivity) and absolute timeouts (forcing a re-login after a set duration, such as 8 hours).

$timeoutDuration = 1800; // 30 minutes

if (isset($_SESSION['last_activity']) && (time() - $_SESSION['last_activity'] > $timeoutDuration)) {
    session_unset();
    session_destroy();
    header("Location: login.php");
    exit();
}
$_SESSION['last_activity'] = time();