Skip to content

什么是会话固定攻击?Spring Security 怎么防御? ​

  • 标签:Spring Security、认证授权、后端

🏛 业务架构师 ​

会话固定攻击(Session Fixation):攻击者先获取一个合法的 Session ID(自己访问拿到、或构造 JSESSIONID=abc 链接诱骗受害者点击),想办法让受害者用这个已知 ID 登录(诱导点击带 SID 的链接、或利用子域 Cookie 注入)——登录成功后该 Session 升级为"已认证",而攻击者拿着同一个 ID 直接就是登录状态。核心:受害者登录前后 Session ID 不变,攻击者"预埋"的 ID 被合法化。

Spring Security 的防御:登录认证成功时自动更换 Session ID(changeSessionId,Servlet 3.1+ 默认策略)——旧 ID 立即作废,攻击者预埋的 ID 变废纸。对应的可配策略:changeSessionId(默认)、migrateSession(换 ID 但迁移旧数据)、newSession(全新会话)、none(禁用防护,千万别)。默认在 AbstractAuthenticationProcessingFilter 成功处理后由 SessionAuthenticationStrategy 执行。

配套的会话安全四件套(一起答显完整):

  1. 会话固定防护(上述);
  2. 会话超时:30 分钟不活动失效(server.servlet.session.timeout)——缩小被劫持窗口;
  3. 同账号并发控制:maximumSessions 限制多端登录(支付类 App 常要求单端在线,挤掉旧会话);
  4. Cookie 安全属性:HttpOnly(JS 读不到,防 XSS 偷 SID)+ Secure(仅 HTTPS 传输)+ SameSite(缓解 CSRF)。

业务视角:Spring Security 的默认配置已含会话固定防护——真正的风险是团队自建登录时忘了这层(直接 setAttribute 就完事)。金融、政务类项目过安全测评(等保)时"会话固定"是必查项,审计问答就一句:"登录成功后是否更换 Session ID?"——框架默认 + 不乱关配置,这分白送。

🏗 系统架构师 ​

会话固定攻击(Session Fixation)= 攻击者预先获取/设定一个会话 ID,诱导受害者用该 ID 登录,从而劫持其会话:

  • 攻击路径:攻击者在 bank.com 拿到一个 sessionId(自建会话或预测/注入);通过钓鱼链接(URL 带 sessionId:bank.com;jsessionid=abc123)、恶意脚本设 Cookie(子域 cookie 注入)等手段让受害者浏览器使用这个已知 ID 登录;登录成功后该 sessionId 升级为"已认证"——攻击者用同一个 ID 即可冒充受害者(他知道 ID,登录是别人做的)。

Spring Security 的防御(默认开启,机制是"换 ID"):

  • 登录成功后重建会话(session fixation protection):SessionManagementConfigurer 默认 changeSessionId(Servlet 3.1+)——认证成功瞬间调用 request.changeSessionId():旧 ID 作废、发新 ID——攻击者手里的旧 ID 失去价值(内容迁移到新 ID,但 ID 本身换了)。可选项 newSession(新会话不拷数据)/migrateSession(拷贝数据)/none(禁用防护,几乎不该用);
  • 配合防线:Cookie 防(HttpOnly 防脚本读、Secure 防窃听);URL 重写禁用(不放 sessionId 进 URL——URL 会进日志/Referer 泄露);登录后重要操作再认证(支付输密码——纵深防御)。

架构师视角的引申:会话固定是"身份与载体绑定时机"问题——认证事件( privilege 变化)必须触发载体更换(ID 轮换),凡是"权限提升后载体不变"的设计都有同构风险:JWT 场景的对应实践是登录后签发新令牌(不复用登录前的任何令牌材料)、OAuth 回调后作废中间状态(code/state 一次性)。Spring Security 的默认值已防住,但自研认证流程时这是必查项(很多手写登录的漏洞就在这:认证成功还沿用登录页建立的匿名 session)。

💻 全栈开发专家 ​

会话固定攻击(Session Fixation):攻击者预先拿到一个会话 ID(自己访问应用获得、或构造 JSESSIONID=abc 让受害者通过钓鱼链接/子域名 Cookie 注入带上这个 ID)→ 诱导受害者用这个已知的 sessionId 登录 → 登录成功后 sessionId 不变 → 攻击者拿同一 ID 直接冒充已登录用户。

核心在于「登录前后 sessionId 不变」这个漏洞条件。

Spring Security 的防御(默认开启,3.x 起内置):认证成功时改变会话标识——SessionAuthenticationStrategy 组合里的 ChangeSessionIdAuthenticationStrategy(Servlet 3.1+,默认)在登录成功瞬间调用 request.changeSessionId():旧 ID 作废、属性保留,攻击者手里的旧 ID 失效。

四种防御策略(按强度):

  • changeSessionId()(默认):换 ID 保数据——推荐;
  • newSession():全新会话(清属性,防旧属性里藏脏东西);
  • migrateSession():新会话+迁移属性(老容器兼容);
  • none():不防(绝不该设)。

显式配置与相关强化:

java
http.sessionManagement(s -> s
    .sessionFixation(f -> f.newSession())      // 显式声明(默认已防护)
    .maximumSessions(1)                        // 单设备登录
    .maxSessionsPreventsLogin(false)          // 后登录踢前登录
    .sessionRegistry(registry));

工程检查清单:

  1. 别手动 session.setId() 绕过策略;
  2. 登录成功后重建会话之外,提权操作(改密、绑手机)也应失效旧会话;
  3. Cookie 加 HttpOnly; Secure; SameSite=Strict——子域 Cookie 注入是固定攻击的现实通道,SameSite 顺手压制;
  4. 老系统迁移(自研认证没换 session id 的)重点回归这一项——Spring Security 的默认防护只保护走它认证流程的登录,自己 Controller 里写 session.setAttribute(user) 的野路子登录不在保护范围。