Understanding PHP Sessions
Sessions allow you to persist user data across multiple page requests. Unlike cookies (which store data on the client side), sessions store data on the server while giving the client a session ID.
How Sessions Work
- Client makes first request to server
- Server creates a unique session ID (e.g., PHPSESSID=abc123)
- Server stores session data in a temporary file (typically in /tmp/ on Linux)
- Session ID is sent to client via cookie or URL
- Subsequent requests include the session ID to access stored data
Basic Session Usage
Starting a Session
<?php
// Must be called before any output
session_start();
// Store data in session
$_SESSION['username'] = 'john_doe';
$_SESSION['last_login'] = time();
// Access session data
echo "Welcome back, " . $_SESSION['username'];
?>
Destroying a Session
<?php
session_start();
// Remove all session variables
session_unset();
// Destroy the session
session_destroy();
// Optional: Delete session cookie
if (ini_get("session.use_cookies")) {
$params = session_get_cookie_params();
setcookie(
session_name(),
'',
time() - 42000,
$params["path"],
$params["domain"],
$params["secure"],
$params["httponly"]
);
}
?>
Critical Session Security Measures
1. Secure Session Configuration
Always set these in php.ini or via ini_set():
; Only transmit cookies over HTTPS
session.cookie_secure = 1
; Prevent JavaScript access to session cookie
session.cookie_httponly = 1
; Use strict session ID mode
session.use_strict_mode = 1
; Regenerate ID on login
session.regenerate_id = 1
; Set custom session save path (not /tmp)
session.save_path = "/var/www/secure_sessions"
2. Session Fixation Protection
session_start();
// Regenerate ID when privilege level changes
if (!isset($_SESSION['created'])) {
session_regenerate_id(true);
$_SESSION['created'] = time();
}
// Regenerate every 30 minutes
elseif (time() - $_SESSION['created'] > 1800) {
session_regenerate_id(true);
$_SESSION['created'] = time();
}
3. Session Hijacking Prevention
// Check IP consistency
if (isset($_SESSION['ip']) && $_SESSION['ip'] !== $_SERVER['REMOTE_ADDR']) {
session_unset();
session_destroy();
die("Security violation detected");
}
// Check User-Agent consistency
if (isset($_SESSION['ua']) && $_SESSION['ua'] !== $_SERVER['HTTP_USER_AGENT']) {
session_unset();
session_destroy();
die("Security violation detected");
}
// Set these on first request
if (!isset($_SESSION['ip'])) {
$_SESSION['ip'] = $_SERVER['REMOTE_ADDR'];
$_SESSION['ua'] = $_SERVER['HTTP_USER_AGENT'];
}
4. Session Timeout Control
// Set timeout to 30 minutes
$timeout = 1800;
// Check timeout
if (isset($_SESSION['last_activity']) && (time() - $_SESSION['last_activity'] > $timeout)) {
session_unset();
session_destroy();
header("Location: /login.php?timeout=1");
exit;
}
$_SESSION['last_activity'] = time();
Advanced Session Management
Database-Backed Sessions
class DBSessionHandler implements SessionHandlerInterface {
private $pdo;
public function __construct(PDO $pdo) {
$this->pdo = $pdo;
}
public function open($savePath, $sessionName) {
return true;
}
public function close() {
return true;
}
public function read($id) {
$stmt = $this->pdo->prepare("SELECT data FROM sessions WHERE id = ?");
$stmt->execute([$id]);
return $stmt->fetchColumn() ?: '';
}
public function write($id, $data) {
$stmt = $this->pdo->prepare(
"REPLACE INTO sessions (id, data, access) VALUES (?, ?, ?)"
);
return $stmt->execute([$id, $data, time()]);
}
public function destroy($id) {
$stmt = $this->pdo->prepare("DELETE FROM sessions WHERE id = ?");
return $stmt->execute([$id]);
}
public function gc($maxlifetime) {
$stmt = $this->pdo->prepare(
"DELETE FROM sessions WHERE access < ?"
);
return $stmt->execute([time() - $maxlifetime]);
}
}
// Usage
$pdo = new PDO("mysql:host=localhost;dbname=secure_app", "user", "password");
$handler = new DBSessionHandler($pdo);
session_set_save_handler($handler, true);
session_start();
Redis Session Storage
ini_set('session.save_handler', 'redis');
ini_set('session.save_path', 'tcp://127.0.0.1:6379?auth=secretpassword');
session_start();
Session Best Practices
- Always call session_start() before output
- Regenerate IDs after login/logout
-
Set proper cookie parameters:
session_set_cookie_params([ 'lifetime' => 86400, // 1 day 'path' => '/', 'domain' => $_SERVER['HTTP_HOST'], 'secure' => true, 'httponly' => true, 'samesite' => 'Strict' ]);
- Never store sensitive data in sessions (like passwords)
- Implement idle timeout (e.g., 30 minutes)
- Validate session consistency (IP, User-Agent)
- Use custom session handlers for large-scale apps
- Monitor session activity for anomalies
Common Session Vulnerabilities & Fixes
1. Session Fixation
Attack: Attacker sets a known session ID
Fix: Always regenerate ID after authentication
session_regenerate_id(true);
2. Session Hijacking
Attack: Stealing session IDs via XSS
Fix: Use HttpOnly and Secure flags
session.cookie_httponly = 1
session.cookie_secure = 1
3. Session Prediction
Attack: Guessing valid session IDs
Fix: Use strong session IDs
session.sid_length = 128
session.sid_bits_per_character = 6
4. Session Data Tampering
Attack: Modifying serialized session data
Fix: Store in database with validation
Complete Secure Session Example
<?php
// Initialize secure session
function secure_session_start() {
$session_name = 'SECURE_SESSION';
$secure = true; // HTTPS only
$httponly = true; // No JS access
// Force cookies to be secure
ini_set('session.cookie_secure', 1);
ini_set('session.cookie_httponly', 1);
ini_set('session.use_strict_mode', 1);
// Set custom cookie params
session_set_cookie_params([
'lifetime' => 86400,
'path' => '/',
'domain' => $_SERVER['HTTP_HOST'],
'secure' => $secure,
'httponly' => $httponly,
'samesite' => 'Strict'
]);
session_name($session_name);
session_start();
// Regenerate ID if too old
if (!isset($_SESSION['created'])) {
session_regenerate_id(true);
$_SESSION['created'] = time();
} elseif (time() - $_SESSION['created'] > 1800) {
session_regenerate_id(true);
$_SESSION['created'] = time();
}
// Validate against hijacking
if (!isset($_SESSION['ip'])) {
$_SESSION['ip'] = $_SERVER['REMOTE_ADDR'];
}
if ($_SESSION['ip'] !== $_SERVER['REMOTE_ADDR']) {
session_unset();
session_destroy();
die("Security violation");
}
}
secure_session_start();
?>