先记住这个答案
Session 固定攻击中,攻击者先用自己的会话 ID 发给受害者,诱使其登录,若登录后不更换 ID,攻击者就能用同一 ID 进入受害者会话。Session 劫持是攻击者通过窃听、XSS 等方式获取受害者当前会话 ID 后直接冒充。重新生成 Session ID 能切断攻击者已知或已持有的 ID 与用户认证状态的绑定关系,使旧 ID 失效。
- 固定攻击靠预先放置的 ID 骗登录
- 劫持靠窃取当前有效 ID 直接冒充
- 登录后换 ID 使旧 ID 失去认证绑定
两种攻击路径的区分
Session 固定攻击的核心是攻击者让受害者使用一个攻击者已知的会话 ID。攻击者先向应用申请一个合法会话,得到 ID(如 SID=abc),然后通过 URL 或脚本使受害者的请求携带这个 ID。受害者若在这个过程中登录,应用若未轮换 ID,认证后会话仍绑定在 SID=abc 上。攻击者随后用自己的浏览器携带 SID=abc 访问应用,就能以受害者身份操作。
Session 劫持的路径不同:攻击者不预先放置 ID,而是通过抓包、XSS 窃取 Cookie、恶意日志等手段拿到受害者当前已经认证的会话 ID。只要 ID 未过期,攻击者直接重放该 ID 即可冒用。劫持不依赖登录动作,攻击发生在认证之后。重新生成 ID 对劫持也有缓解意义:若在权限变更或定期轮换后旧 ID 失效,攻击者窃取的旧 ID 就作废。
以论坛登录为例的决策
假设一个论坛使用 Cookie 保存 Session ID,用户未登录时先访问了恶意链接,链接携带攻击者设定的 SID=attacker_known。用户点击后应用识别 SID,但未认证,因此显示登录页。用户输入正确的用户名和密码登录,应用此时检查 Session 状态:若继续沿用 SID=attacker_known 并标记为已登录,攻击者立刻用该 ID 访问用户后台。正确做法是登录成功后调用 session_regenerate_id(),生成全新的随机 ID,并删除旧会话数据。
具体处理:登录验证通过后,先销毁旧会话,再创建新会话并复制必要的用户 ID 和角色,同时设置新 Cookie。这样即使攻击者预置的 ID 仍有效,它只对应未认证的匿名会话,无法访问用户资源。实际编码时要注意在重定向之前完成 Cookie 更新,避免响应头冲突。此外,权限提升或修改密码后也应同样处理,因为攻击者可能已经掌握了当前 ID。
防御的适用边界
重新生成 Session ID 并不能抵御所有劫持:如果攻击者在用户登录后才窃取新生成的 ID,那么轮换无法阻止。因此必须结合 Cookie 的 Secure、HttpOnly 属性、传输加密和 XSS 防护。同时,轮换策略也有代价:如果过于频繁,会让用户被强制登出或丢失购物车数据。所以一般只在认证、权限变更、密码修改等关键点执行。
对于无状态 JWT,重新生成 ID 的概念不直接适用,因为 JWT 没有服务端会话,但逻辑类似:登录后应签发新令牌,并配合短有效期或维护黑名单使旧令牌尽快失效。若应用同时使用 Session 和 JWT,要明确轮换哪一层。另一个边界是:SAML 等联合登录后的本地会话也需要轮换,否则攻击者可能通过预置的本地会话 ID 绑定 SSO 登录结果。
容易答错的地方
- 认为固定攻击和劫持是同一回事
- 两者攻击路径不同,固定攻击发生在认证前,利用服务端不验证 ID 来源;劫持发生在认证后,依赖窃取已认证 ID。防御措施有重叠,但理解差异才能正确修复。
- 只在登录成功时生成新 ID,忽略其他权限变化
- 如果用户在登录后从普通成员提升为管理员,或修改了密码,却没有轮换 ID,攻击者若已掌握旧 ID,就能在权限变化后继续冒充。所有关键状态变化都应考虑轮换。
面试官还会怎么问?
如果攻击者能用 XSS 窃取 Cookie,重新生成 ID 还有意义吗?
有意义。XSS 窃取的是当前 ID,若在登录时已轮换,攻击者在登录前预置的固定 ID 就无效;但登录后注入的 XSS 仍能窃取新 ID,所以必须同时用 HttpOnly 和 CSP 降低 XSS 风险。
Session 固定攻击是不是只要用 HTTPS 就能防止?
不能。HTTPS 能防止网络嗅探,但攻击者可以通过 URL 传递 ID 或利用跨站脚本放置 Cookie,HTTPS 不阻止这些。固定攻击的核心是服务端不在认证后变更 ID。
在 Redis 中存储 Session,轮换 ID 时要怎么操作?
删除旧键并写入新键,使用新 ID 作为键名(或唯一索引),同时注意原子性,防止并发请求同时操作导致状态丢失。可用 Lua 脚本确保删除和新建整体完成。
参考资料
示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。