开篇故事:一张永远有效的“万能门票”
某在线教育平台的学员小王收到一封“课程优惠券”邮件,点击链接后正常登录账户。
一周后,小王发现自己的VIP课程被陌生人观看,学习记录中出现不明设备登录。
真相:攻击者通过邮件链接植入“固定会话ID”,如同复制了小王的账户。
一、会话管理基础:为什么需要“会话ID”?
1. 会话机制的本质
-
会话ID:服务器为用户分配的临时身份凭证,类似电影院门票 -
核心作用: 访问网站/用户登录[非必须] → 获得会话ID → 后续请求携带ID → 服务器识别用户身份
一般来说,当用户访问一个网站时,服务器会为每个用户创建一个会话,无论用户是否登录。这个会话ID通常是在用户第一次访问网站时生成的,用于跟踪用户的状态。
例如,未登录的用户可能将商品添加到购物车,这时候服务器需要维护一个会话,所以即使未登录,也会分配会话ID。这种情况下,登录前的会话ID是存在的,但权限较低,仅用于临时状态保存。
2. 正常会话流程
-
用户首次访问: -
客户端请求无会话ID → 服务端生成 SESSID=abcd
→ 通过Set-Cookie
下发。 -
客户端后续请求: -
携带 Cookie: SESSID=abcd
→ 服务端查询会话存储 → 处理业务逻辑。 -
用户登录: -
服务端销毁旧会话ID → 生成新ID(如 SESSID=xyz987
) → 更新Cookie。
关键点:
-
每次登录生成新会话ID -
会话ID通过Cookie或URL传递
二、会话固定攻击原理:攻击者如何“复制钥匙”?
1. 攻击四步走
-
生成固定会话ID:攻击者访问网站,获取特定会话ID(如SESSID=abcd) -
诱导用户使用该ID:通过邮件/钓鱼页面发送含SESSID=abcd的链接 -
用户登录绑定会话:用户点击链接登录,服务器将账户与abcd关联 -
攻击者接管会话:攻击者使用abcd访问网站,直接获得用户权限
2. 漏洞代码示例(危险!)
// 错误示例:允许通过URL参数指定会话ID if(isset($_GET['sessid'])) { session_id($_GET['sessid']); // 接受外部传入的会话ID } session_start(); // 用户登录后未重置会话ID if($login_success) { $_SESSION['user'] = $username; }
攻击Payload:
https://example.com/login.php?sessid=abcd
三、真实案例:血淋淋的教训
案例1:某银行转账功能沦陷
-
漏洞点:会话ID通过URL传递且登录后不更新 -
攻击过程: -
攻击者生成链接: bank.com/transfer?sessid=1234
-
诱导客服人员点击链接并登录后台 -
攻击者访问 bank.com/home?sessid=1234
直接进入客服账户 -
损失:非法转账1200万元
案例2:政府OA系统公文泄露
-
漏洞点:Cookie中会话ID长期有效且未绑定IP -
攻击过程: -
攻击者通过XSS漏洞窃取会话ID -
在不同IP上使用该ID持续访问系统三个月 -
后果:数百份机密文件外泄
四、防御指南:四道防线锁死会话安全
防线1:登录时强制生成新会话ID
// 正确示例:登录成功后重置会话ID session_start(); if($login_success) { session_regenerate_id(true); // 销毁旧会话,生成新ID $_SESSION['user'] = $username; }
原理:
-
用户登录前使用临时会话ID(攻击者可控制) -
登录后切换为高权限新ID(攻击者无法获取)
防线2:会话ID绑定多因素
# Django示例:会话ID绑定IP+UserAgent from django.contrib.sessions.backends.db import SessionStore defcreate_session(request): s = SessionStore() s['ip'] = request.META['REMOTE_ADDR'] s['ua'] = request.META['HTTP_USER_AGENT'] s.save() return s.session_key
验证逻辑:
if request.session['ip'] != current_ip: logout_user()
防线3:严格传输安全策略
# 响应头设置(防止会话ID被嗅探) Set-Cookie: PHPSESSID=abcd1234; Secure; HttpOnly; SameSite=Strict
-
Secure:仅通过HTTPS传输 -
HttpOnly:禁止JavaScript读取 -
SameSite:阻止跨站请求伪造(CSRF)
若想了解CSRF的漏洞原理,可以参阅《「 典型安全漏洞系列 」03.跨站请求伪造CSRF详解》。
防线4:主动销毁过期会话
// Spring Security配置会话超时 @Configuration@EnableWebSecuritypublicclassSecurityConfigextendsWebSecurityConfigurerAdapter{@Overrideprotectedvoidconfigure(HttpSecurity http)throws Exception { http.sessionManagement() .invalidSessionUrl("/login?expired") // 会话失效跳转 .maximumSessions(1) // 最大会话数 .maxSessionsPreventsLogin(true) // 阻止新登录 .expiredUrl("/login?expired"); // 会话过期跳转 }}
策略:
-
用户注销后立即销毁会话 -
15分钟无操作自动过期( server.servlet.session.timeout=900
)
五、实战演练:手把手检测你的网站
检测步骤:
1、访问网站:通过F12查看登录前Cookie中的会话ID
2、观察登录后响应:若生成新ID如JSESSIONID=new_id
→ 防御生效
自动化工具推荐:
-
OWASP ZAP:主动扫描会话固定漏洞 -
Acunetix:深度检测会话管理缺陷
推荐阅读:
六、总结:会话安全是身份防护的第一关
-
攻击成本极低:一个链接即可发起攻击,无需复杂技术 -
防御要点总结:✅ 登录必换新ID✅ 会话绑定多维度信息✅ 传输过程全加密✅ 闲置超时必销毁
行动呼吁:
打开你的网站,尝试在登录前后检查Cookie中的会话ID是否变化? 欢迎评论区晒出测试结果,看看谁的防御最牢固!
关注我,带你用“人话”读懂技术硬核! 🔥
原文始发于微信公众号(全栈安全):一文揭秘会话固定攻击全流程,4 道防线锁死会话安全!
免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论