A session ID works like a key to a user’s account. If an attacker steals or guesses it, they can pretend to be that user. These habits keep the key safe.
1. Secure cookie flags
<?php
session_set_cookie_params([
"lifetime" => 0, // until the browser closes
"path" => "/",
"secure" => true, // HTTPS only
"httponly" => true, // JavaScript cannot read it
"samesite" => "Lax", // limits cross-site sending
]);
session_start();
- HttpOnly stops scripts from reading the ID, so an XSS bug cannot steal it directly.
- Secure keeps the cookie off unencrypted connections.
- SameSite reduces the risk of CSRF.
2. Strict mode
<?php
ini_set("session.use_strict_mode", "1");
ini_set("session.use_only_cookies", "1");
use_strict_moderefuses a session ID that the server did not create. That helps against session fixation.use_only_cookiesstops session IDs from being accepted in a URL (?PHPSESSID=...), where they leak through logs and the Referer header.
3. Regenerate the ID at login
Create a new ID whenever the user logs in or gains privileges. The old ID becomes useless.
<?php
session_regenerate_id(true); // true deletes the old session data
$_SESSION["user_id"] = $user["id"];
See Session Fixation for the attack this prevents.
4. Timeouts
Log people out after a period of inactivity, and set an absolute limit too.
<?php
function sessionIsValid(array $session, int $now, int $idle = 1800, int $absolute = 28800): bool
{
return ($now - ($session["last_activity"] ?? 0)) <= $idle
&& ($now - ($session["started_at"] ?? 0)) <= $absolute;
}
$session = ["started_at" => 1000, "last_activity" => 2000];
var_dump(sessionIsValid($session, 2500)); // 500 seconds idle
var_dump(sessionIsValid($session, 9000)); // 7000 seconds idle
bool(true)
bool(false)
5. Store little, and nothing secret
Keep an ID and a few flags. If the session store is ever exposed, there should be little to steal.
6. A safe start function
<?php
function startSecureSession(): void
{
if (session_status() !== PHP_SESSION_NONE) {
return;
}
ini_set("session.use_strict_mode", "1");
ini_set("session.use_only_cookies", "1");
session_set_cookie_params([
"lifetime" => 0,
"path" => "/",
"secure" => !empty($_SERVER["HTTPS"]) && $_SERVER["HTTPS"] !== "off",
"httponly" => true,
"samesite" => "Lax",
]);
session_start();
$now = time();
$_SESSION["started_at"] ??= $now;
if (($now - ($_SESSION["last_activity"] ?? $now)) > 1800) {
$_SESSION = [];
session_regenerate_id(true);
$_SESSION["started_at"] = $now;
}
$_SESSION["last_activity"] = $now;
}
7. Use HTTPS everywhere
Without HTTPS, anyone on the same network can read the session cookie. Redirect all traffic to HTTPS and consider the Strict-Transport-Security header.
8. Logout properly
Clear $_SESSION, expire the cookie and call session_destroy(). See Destroying a Session.
9. Do not rely on the IP address or User-Agent
Binding a session to an IP address breaks for people on mobile networks, whose address changes. A User-Agent check is easy to fake. They add little protection and can lock out real users.
Quick checklist
httponly,secure,samesiteon the cookie- Strict mode and cookies only
- New ID at login and privilege changes
- Idle and absolute timeouts
- Small, non-secret session data
- HTTPS and a clean logout