本文目录导读:

针对 PHP 项目中 Session 与 Cookie 的安全性,这是一个非常重要的议题,下面从配置、攻击防御、最佳实践等多个维度进行详细梳理。
基础概念与关系
- Cookie:存储在客户端(浏览器)的键值对数据,通常用于存储 Session ID 或其他持久化信息。
- Session:存储在服务端的数据(文件、数据库、Redis 等),通过前端发送的 Session ID(通常存放在 Cookie 中)来识别用户。
- 核心风险:Session ID 被窃取(即会话劫持),攻击者可以伪造用户身份,Cookie 不安全,可能导致敏感信息泄露或 CSRF(跨站请求伪造)。
Session 安全配置
使用强 Session ID
PHP 默认的 Session ID 生成算法在较新版本(>= 7.1)中已经足够安全,但需要确保:
// 确保使用强算法(PHP 7.1+ 默认)
ini_set('session.use_strict_mode', 1); // 拒绝未初始化的 Session ID
ini_set('session.sid_length', 48); // 增加长度(最小 22,建议 48)
ini_set('session.sid_bits_per_character', 6); // 使用更多字符集
Session 生命周期管理
- 设置合理过期时间:限制 Session 存活时间,降低被重放的风险。
- 固定时间后重新生成 Session ID:防止 Session 固定攻击。
session_start();
// 登录成功后必须重新生成 Session ID
if ($login_success) {
session_regenerate_id(true); // 删除旧 Session 文件
$_SESSION['user_id'] = $user['id'];
$_SESSION['login_time'] = time();
}
// 检查是否超时(30 分钟无操作)
if (isset($_SESSION['last_activity']) && (time() - $_SESSION['last_activity'] > 1800)) {
session_destroy();
header('Location: login.php');
exit;
}
$_SESSION['last_activity'] = time();
Session 存储安全
- 避免默认文件存储:尤其当服务器存在文件包含漏洞时,Session 文件可能在
/tmp/目录下被读取,建议使用 Redis 或 Memcached 存储 Session。// 使用 Redis 存储 Session ini_set('session.save_handler', 'redis'); ini_set('session.save_path', 'tcp://127.0.0.1:6379?auth=password&prefix=PHPSESSIONS:'); - 限制 Session 文件权限:如果必须使用文件存储,确保
/tmp/或自定义路径权限为 700,且不被其他用户读取,配合 php.ini 配置:session.save_path = "/var/lib/php/sessions"
防范 Session 固定攻击
- 用户登录成功后必须调用
session_regenerate_id()。 - 接受来自 URL 的 Session ID (通过
session.use_trans_sid控制):session.use_only_cookies = 1 ; 禁止通过 URL 传递 Session ID session.use_trans_sid = 0 ; 关闭透明 Session ID 支持
防范 Session 劫持
- 用户代理绑定(非严格,仅提高门槛):
// 登录时绑定 User-Agent 和 IP 前缀 $_SESSION['session_fingerprint'] = md5($_SERVER['HTTP_USER_AGENT']); // 每次请求验证 if ($_SESSION['session_fingerprint'] !== md5($_SERVER['HTTP_USER_AGENT'])) { session_destroy(); exit('Session hijack detected.'); }- 注意:绑定 IP 可能会导致正常用户因 IP 切换(如移动网络)而退出,所以通常只绑定 User-Agent 或结合 IP 段(前 3 位)。
Cookie 安全配置
关键标记(Flags)设置
通过 session_set_cookie_params() 或 PHP 配置文件设置:
session_start();
session_set_cookie_params([
'lifetime' => 0, // 为 0 表示浏览器关闭即失效(建议)
'path' => '/',
'domain' => 'example.com', // 尽量指定,不要留空
'secure' => true, // 仅通过 HTTPS 传输
'httponly' => true, // 禁止 JavaScript 访问(防止 XSS 窃取)
'samesite' => 'Strict' // CSRF 防护:Strict | Lax | None
]);
Secure:必须为true,防止 HTTP 中间人获取 Session ID。HttpOnly:必须为true,防止 XSS 漏洞窃取 Cookie。SameSite:强烈推荐'Strict'或'Lax'。
不要存储敏感信息
- 永远不要在 Cookie 中存储密码、明文用户数据,Cookie 只能存放 Session ID 或非敏感标识符。
- 如果必须存储数据(记住我”的 Token),必须:
- 使用加密 Token(如
random_bytes(32)生成的序列)。 - 服务端 Hash 存储该 Token。
- 设置较短有效期(如 7 天)。
- 使用加密 Token(如
销毁机制
// 完全登出 $_SESSION = []; setcookie(session_name(), '', time() - 3600, '/'); // 删除 Cookie session_destroy();
常见攻击与防御
| 攻击类型 | 原理 | 防御措施 |
|---|---|---|
| XSS(窃取 Cookie) | 通过注入 <script>alert(document.cookie)</script> 窃取 HttpOnly Cookie 以外的内容。 |
HttpOnly 标记、输出转义(htmlspecialchars)、CSP 策略。 |
| CSRF(跨站请求) | 诱导用户在已登录状态下点击恶意链接,执行非意愿操作。 | SameSite=Lax 或 Strict、CSRF Token(每个表单唯一,服务端验证)、Referer 验证。 |
| Session 固定 | 攻击者给用户一个已知的 Session ID,用户登录后依然使用该 ID。 | session_regenerate_id(true) 登录后重新生成。 |
| 中间人攻击 | 窃听网络流量,获取未加密的 Cookie。 | 全站 HTTPS(包括静态资源)、Secure 标记、HSTS 头。 |
| Cookie 窃取(网络层) | 同中间人攻击。 | HTTPS + Secure 标记。 |
最佳实践总结
- 全站 HTTPS:这是所有安全的基础。
- Cookie 标记:
Secure = trueHttpOnly = trueSameSite = Lax(或Strict,根据业务场景)
- Session 配置:
session.use_only_cookies = 1session.use_strict_mode = 1- 登录后
session_regenerate_id(true)
- Session 存储:使用 Redis/数据库,避免默认文件。
- 数据最小化:Session 中不存敏感数据(如密码),使用加密 Token 替代。
- 定期清理:设置 Session 过期时间(
session.gc_maxlifetime),并配置垃圾回收概率。 - 防止 XSS:所有输出使用
htmlspecialchars($data, ENT_QUOTES, 'UTF-8')。 - 防止 CSRF:除了
SameSite,高安全场景必须使用 CSRF Token。
参考配置(php.ini 或代码)
; session 安全相关 session.use_strict_mode = 1 session.use_cookies = 1 session.use_only_cookies = 1 session.use_trans_sid = 0 session.cookie_httponly = 1 session.cookie_secure = 1 session.cookie_samesite = "Lax" session.sid_length = 48 session.sid_bits_per_character = 6 session.cache_limiter = nocache