PHP会话固定攻击(Session Fixation Attack)是一种常见的Web安全威胁,攻击者通过让受害者使用攻击者已知的Session ID,从而在受害者登录后获取其会话控制权。

防护方案
核心防护:登录后强制更换Session ID
<?php // 用户登录成功后立即执行 session_regenerate_id(true); // 生成新的Session ID并删除旧ID
完整的安全登录示例
<?php
session_start();
// ... 验证用户凭证 ...
if ($login_success) {
// 清除所有session数据
$_SESSION = array();
// 删除会话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"]
);
}
// 彻底销毁旧会话
session_destroy();
// 开始新会话
session_start();
// 生成新会话ID
session_regenerate_id(true);
// 设置用户会话
$_SESSION['user_id'] = $user_id;
$_SESSION['login_time'] = time();
$_SESSION['fingerprint'] = generateFingerprint(); // 见下文
}
增强安全措施
设置安全的Session配置
<?php
// php.ini 或代码中的配置
ini_set('session.use_strict_mode', '1'); // 拒绝未初始化的Session ID
ini_set('session.use_only_cookies', '1'); // 仅使用Cookie传递Session ID
ini_set('session.cookie_httponly', '1'); // 禁止JavaScript访问Session Cookie
ini_set('session.cookie_secure', '1'); // HTTPS下使用
ini_set('session.cookie_samesite', 'Lax'); // 防CSRF
ini_set('session.gc_maxlifetime', '1800'); // 会话超时时间(秒)
用户指纹验证
<?php
function generateFingerprint() {
// 使用用户特征生成指纹
return hash('sha256',
$_SERVER['HTTP_USER_AGENT'] .
getClientIP() .
(isset($_SERVER['HTTP_ACCEPT_LANGUAGE']) ? $_SERVER['HTTP_ACCEPT_LANGUAGE'] : '')
);
}
function validateFingerprint() {
if (isset($_SESSION['fingerprint'])) {
return $_SESSION['fingerprint'] === generateFingerprint();
}
return false;
}
// 每次请求验证
if (!validateFingerprint()) {
session_regenerate_id(true); // 指纹不匹配时重新生成
}
完整的防护类
<?php
class SessionSecurity {
public static function secureSessionStart() {
// 严格模式
ini_set('session.use_strict_mode', '1');
ini_set('session.use_only_cookies', '1');
ini_set('session.cookie_httponly', '1');
ini_set('session.cookie_secure', '1');
ini_set('session.cookie_samesite', 'Lax');
session_start();
// 验证Session ID是否由服务器生成
if (!isset($_SESSION['initiated'])) {
session_regenerate_id(true);
$_SESSION['initiated'] = true;
$_SESSION['ip'] = getClientIP();
$_SESSION['user_agent'] = $_SERVER['HTTP_USER_AGENT'];
}
// 验证IP和User-Agent
if ($_SESSION['ip'] !== getClientIP() ||
$_SESSION['user_agent'] !== $_SERVER['HTTP_USER_AGENT']) {
self::destroySession();
session_start();
session_regenerate_id(true);
}
}
public static function loginSession($userId) {
// 登录成功后重置
self::destroySession();
session_start();
session_regenerate_id(true);
$_SESSION['user_id'] = $userId;
$_SESSION['ip'] = getClientIP();
$_SESSION['user_agent'] = $_SERVER['HTTP_USER_AGENT'];
$_SESSION['last_activity'] = time();
$_SESSION['initiated'] = true;
}
public static function destroySession() {
$_SESSION = array();
if (ini_get("session.use_cookies")) {
$params = session_get_cookie_params();
setcookie(session_name(), '', time() - 42000,
$params["path"], $params["domain"],
$params["secure"], $params["httponly"]
);
}
session_destroy();
}
private static function getClientIP() {
// 获取真实IP(考虑代理)
$ip = $_SERVER['REMOTE_ADDR'];
if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
$ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
}
return $ip;
}
}
// 使用示例
SessionSecurity::secureSessionStart();
其他重要预防措施
<?php
// 定期更换Session ID(防止固定攻击的持久化)
if (isset($_SESSION['last_activity']) &&
(time() - $_SESSION['last_activity'] > 600)) {
session_regenerate_id(true);
$_SESSION['last_activity'] = time();
}
// 设置合理的过期时间
$_SESSION['expire_time'] = time() + 3600; // 1小时
if (time() > $_SESSION['expire_time']) {
SessionSecurity::destroySession();
header('Location: /login.php');
exit();
}
安全建议清单
- ✅ 强制更换Session ID:登录成功、权限变更时务必调用
session_regenerate_id(true) - ✅ 启用Strict Mode:
session.use_strict_mode = 1,拒绝未初始化的Session ID - ✅ 使用HTTPS:确保
session.cookie_secure = 1 - ✅ 设置Session过期时间:避免会话无限期有效
- ✅ 使用随机和长的Session ID:PHP默认使用哈希算法,保持默认即可
- ✅ 验证User-Agent和IP:但注意IP变化需谨慎处理
- ✅ SameSite属性:设置为
Lax或Strict - ❌ 不要接受URL传递的Session ID
- ❌ 不要长期使用同一个Session ID
更高级的防护:双重验证
<?php
// 生成随机会话令牌
$session_token = bin2hex(random_bytes(32));
$_SESSION['session_token'] = $session_token;
// 在前端页面保存(通过cookie或header)
setcookie('session_token', $session_token, [
'expires' => time() + 3600,
'path' => '/',
'secure' => true,
'httponly' => true,
'samesite' => 'Lax'
]);
// 每次请求验证
if (isset($_COOKIE['session_token']) &&
isset($_SESSION['session_token']) &&
$_COOKIE['session_token'] !== $_SESSION['session_token']) {
// 令牌不匹配,可能是攻击
SessionSecurity::destroySession();
header('Location: /login.php');
exit();
}
关键点在于:登录成功后必须更换Session ID,这是最核心、最有效的防护措施,其他措施都是辅助性的,可以显著提高安全性,建议综合使用上述所有方法,构建多层次的防护体系。