会话固定是指受害者用攻击者准备好的 ID 完成了登录。本文讲解它的原理与可靠的防御方法(不公开攻击步骤)。
为什么会成立(原理)
原因在于「登录前后 session ID 不变」这种设计。只要它不变,攻击者在登录前就已知的 ID,在登录后仍会被当作「已认证」而通行。
它有时会与 CSRF 或 XSS 组合使用,但会话固定本身的本质只有一点:「登录时没有重新生成 ID」。
日本 IPA(信息处理推进机构)的指南「安全なウェブサイトの作り方」把它视为「会话管理不当」的一种形式,并指出所有带登录功能的网站都需要留意。其中给出的根本解决办法,是在登录成功后开始一个新的会话。
会话固定、会话劫持与 CSRF 的区别
这三者容易混淆。它们都滥用登录状态,但攻击者手里有什么、哪种防御有效,各不相同。
| 项目 | 会话固定 | 会话劫持 | CSRF |
|---|---|---|---|
| 攻击者手里有什么 | 自己准备的 ID(一开始就已知) | 窃取到的、已发出的 ID | 根本没有 ID |
| 受害者身上发生什么 | 用被埋下的 ID 登录 | 已发出的 ID 被窃取 | 被迫发送非本意的请求 |
| 主要防御 | 登录时重新生成 ID | HttpOnly/Secure、防范 XSS、缩短有效期 | CSRF token、SameSite |
会话固定是其中靠「登录时重新生成 ID」这一步就能从根本上解决的一种。
防御:登录时重新生成 ID
登录成功时重新生成 session ID(最重要)
不接受通过 URL 指定 session ID
设置 Cookie 属性
HttpOnly(JS 无法读取)、Secure(仅 HTTPS)、SameSite(抑制从其他站点发起的发送)。把相关的劫持路径一并削弱。设置合理的失效
OWASP 的 Session Management Cheat Sheet 建议在每次权限变化时重新生成 ID,并采用只接受服务器自己生成的 ID 的「严格(strict)」机制。如果采用会直接沿用外部带入的未知 ID 的「宽松(permissive)」机制,就留下了接受攻击者所选 ID 的余地。
常见框架的默认行为
大多数框架只要使用其标准登录功能,就会自动重新生成 ID。风险出现在自己编写登录处理,或关掉了默认保护的时候。
| 环境 | 默认行为 | 自己编写时要确认的点 |
|---|---|---|
| PHP(原生会话) | 不会自动重新生成;session.use_strict_mode 默认为 0 | 登录成功时调用 session_regenerate_id(true);把 use_strict_mode 设为 1 |
| Laravel | 使用入门套件(starter kit)或 Fortify 时,认证时自动重新生成 | 如果手写登录,调用 $request->session()->regenerate() |
| Spring Security | 默认开启(Servlet 3.1 以上为 changeSessionId) | 确认没有人设置了 sessionFixation().none() |
在 PHP 中,通过 URL 传递 session ID 的设置(启用 session.use_trans_sid、禁用 session.use_only_cookies)已在 PHP 8.4 中被弃用。如果沿用旧的配置文件,请重新检查这些设置。
检查自己网站的方法
在你管理的网站上确认以下四点。只需要开发者工具即可。
- 在开发者工具的「Application」(Firefox 为「Storage」)中记下会话 Cookie 的值,登录后再看一次。如果没有变化,说明登录时没有重新生成 ID
- 确认登录页和登录后的 URL 中没有
PHPSESSID=或;jsessionid=之类的 session ID - 在同一面板中确认会话 Cookie 带有
HttpOnly、Secure和SameSite - 登出后把旧的 Cookie 值放回去并重新加载,确认不会再次处于登录状态
Cookie 属性也可以用网站安全检测检查(对象为已验证所有权的网站)。在每次改动登录流程时,把第 1 项写进自动化测试,就能在重新生成被漏掉时立即发现。
本站观点:别关掉框架的『重新生成』
会话固定不需要自己精心设计的对策,靠「登录时重新生成 ID」这一步基本就能防住。重要的是不要不小心关掉、也不要忘记调用框架已经准备好的这个机制。与其自己实现一套会话管理,不如使用经过验证的实现的安全默认行为。本站坚持「认证和密钥相关的东西不自己造、不关掉默认的安全侧」这一原则。与 IDOR 一样,认证、授权相关的关键,就是「靠机制让它每次都生效」。
参考资料(一手来源)
- IPA「安全なウェブサイトの作り方」1.4 セッション管理の不備 — ipa.go.jp(日语;登录后开始新会话、不把 ID 放进 URL)
- OWASP Session Management Cheat Sheet — cheatsheetseries.owasp.org(权限变化时重新生成、严格机制、Cookie 属性)
- MITRE CWE-384: Session Fixation — cwe.mitre.org
- PHP Manual, "Runtime Configuration"(会话)— php.net(
use_strict_mode等的默认值、8.4 的弃用项) - Laravel docs, "HTTP Session" — laravel.com / Spring Security, "Authentication Persistence and Session Management" — docs.spring.io