Session 固定攻击防护
解读
在国内一线/二线互联网公司的 PHP 后端面试中,"Session 固定攻击防护"属于高频安全考点。面试官通常不会只问"怎么防",而是层层递进:
- 能否准确说出攻击原理;
- 能否给出 PHP 侧可落地的代码或配置;
- 能否结合业务场景(登录、支付、sso、多端)权衡利弊;
- 能否把 Session 安全放进整体 SDL(安全开发生命周期)。
回答时切忌只背"session_regenerate_id()"。要把"生成新 ID → 销毁旧 ID → 设置 Cookie 属性 → 配合 CSRFToken → 记录审计日志"完整闭环说出来,才能体现资深水平。
知识点
-
Session 固定攻击原理
攻击者先获取一个合法的 Session ID(如 http://example.com/?PHPSESSID=abc123),诱导受害者使用此 ID 登录;登录后服务端未更换 ID,导致攻击者直接拥有已认证会话。 -
核心防御手段
a) 登录态跃迁时必须 session_regenerate_id(true) —— 参数 true 表示立即删除旧会话文件,避免旧 ID 可用。
b) 设置 session.cookie_httponly = 1,阻断 XSS 读取 Cookie。
c) 设置 session.cookie_secure = 1,强制 HTTPS 传输。
d) 设置 session.cookie_samesite = "Lax" 或 "Strict",防止跨站 Cookie 发送。
e) 使用 session.use_strict_mode = 1,拒绝服务端未初始化的 ID。
f) 给 Session Cookie 绑定用户侧指纹(UA、IP 段、设备号),变更即失效。
g) 登录后把旧 Session 数据整体迁移到新 ID,避免丢购物车等体验问题。
h) 记录"登录→换 ID"审计日志,方便风控与溯源。 -
PHP 版本差异
PHP7.1 起支持 session_create_id(),可在 regenerate 前预生成,避免并发竞争;PHP7.3 起支持 SameSite 原生写法。面试时主动提及版本兼容性,可加分。 -
框架级封装
Laravel 自带 Illuminate\Session\Middleware\AuthenticateSession,可配置 regenerate;Symfony 在 security.yml 中 always_remember_me 与 token 配置也能触发 regenerate。说明"我们不用重复造轮子,但要读懂源码,防止中间层遗漏"。 -
微服务/多终端场景
移动端常用 JWT + Refresh Token,Session 固定问题转化为"Token 固定"。此时要答"登录后签发新 Token,旧 Token 加入 Redis 黑名单",体现举一反三能力。
答案
"Session 固定攻击的本质是让受害者用攻击者已知的 Session ID 完成登录,从而共享会话。PHP 端的完整防护分四步:
第一步,修改 php.ini 基础配置:
session.use_strict_mode = 1
session.cookie_httponly = 1
session.cookie_secure = 1
session.cookie_samesite = "Lax"
第二步,在代码层登录成功瞬间调用:
session_regenerate_id(true); // true 强制删除旧会话文件
// 如需兼容 PHP5.6,可先 session_write_close() 再 regenerate
第三步,把用户侧指纹写入 Session,并在每个请求校验:
_SERVER['REMOTE_ADDR']._SESSION['ip_hash'] !== substr(md5(_SERVER['HTTP_USER_AGENT']), 0, 8)) {
session_unset(); session_destroy(); exit('非法环境');
}
第四步,记录审计日志并配合 CSRF 双令牌:
error_log(date('Y-m-d H:i:s').' UID:'.oldId.'->'.session_id());
通过以上闭环,可在国内电商、CMS、SaaS 等高并发场景下,把 Session 固定攻击面降到几乎为零,同时兼顾用户体验和性能。"
拓展思考
-
当业务使用多域名 SSO(如 .a.com、.b.com)时,SameSite=Lax 会导致跨站 POST 登录态丢失,如何兼顾安全与单点体验?
答:主域统一使用 "SameSite=None; Secure",同时把 Session ID 与 CAS Token 双重校验;对非主域请求再附加 JWT 签名,防止 ID 被篡改。 -
在 PHP-FPM + Redis 集群 的分布式 Session 方案里,regenerate_id(true) 会触发旧 key 删除,瞬间造成 Redis 主库 DEL 压力,如何优化?
答:采用"标记删除"策略:旧 Session 只设置 TTL=60s,由 Redis 自然过期;登录后新 ID 立即写入,读请求优先访问新 ID,60s 后旧 ID 自动失效,避免集中 DEL。 -
如果公司使用 Serverless(如 Baidu CFC、阿里云 FC),PHP 进程寿命极短,文件型 Session 不可用,如何防止固定攻击?
答:把 Session 存入云原生 Redis 或 TableStore,启用 use_strict_mode;登录 regenerate 后,旧 ID 写入黑名单有序集合(zset),过期时间=Token 有效期;网关层统一校验黑名单,实现"无状态 + 高安全"。