文章总结: JWT漏洞利用完全指南详细介绍了7种常见JWT安全漏洞及其利用方法,包括None算法攻击、缺少签名验证、算法混淆攻击、JWK欺骗攻击、密钥ID注入攻击、弱密钥暴力破解和硬编码密钥枚举。文章强调开发者应严格遵循JWT安全最佳实践,避免在生产环境中使用none算法、确保正确验证签名、谨慎处理密钥ID参数、使用强密钥并避免硬编码密钥,以防止认证绕过和注入攻击。
综合评分: 85
文章分类: 渗透测试,漏洞分析,WEB安全
JWT 漏洞利用完全指南
AYOUB
securitainment
2025年11月21日 10:24
中国香港
在 JSON Web Tokens (JWTs) 成为当今应用开发的主流方案之前,Web 应用主要依赖服务器端会话管理,但这种方式在水平扩展方面存在明显瓶颈。JWT 的出现解决了这一问题——它将认证数据从服务器端转移到令牌本身。JWT 具有自包含、无状态、加密签名等特性,几乎能满足应用开发中的所有认证需求。
然而,当开发者忽视最佳实践、为了其他目标而违背标准安全规范时,JWT 漏洞就会随之出现。这类漏洞往往会造成严重的安全缺陷,给受影响的组织及其用户带来高风险。
本文将深入探讨导致 JWT 出现认证绕过和注入攻击漏洞的多种场景,帮助读者理解安全测试的重要性,以及遵循最佳实践的必要性。
让我们开始吧!
什么是 JSON Web Tokens (JWTs)
JSON Web Tokens 通常用于 Web 服务 (如 Web 应用、API 和单页应用) 中的认证会话管理,但它们还有其他常见的应用场景:
- 刷新令牌:用于获取新的会话令牌
- 密码重置和邮箱验证令牌
- 分享链接和邀请链接
- 其他用于提供数据对象 (如文档) 临时访问权限的令牌
登录后 API 发放 JWT 的 HTTP 请求示例
当 JWT 实现存在缺陷时,目标应用就可能面临多种 JWT 攻击威胁,我们将在本文后续部分详细讨论这些攻击方式。在深入探讨漏洞利用之前,让我们先解构一个标准的 JSON Web Token,以便更好地理解这些安全问题是如何产生的。
观看我们的 JWT 攻击视频教程
如果你更喜欢视频学习,欢迎访问我们 YouTube 频道上专门讲解 JWT 漏洞利用的教学视频系列。
解构 JSON Web Tokens
从设计上看,JSON Web Tokens 由三个部分组成:头部 (Header)、载荷 (Payload) 和签名 (Signature)。头部包含指导 JWT 库如何签名和验证令牌的基本信息;载荷包含声明 (Claims) 以及开发者自定义的其他数据属性;签名则用于确保头部和载荷不被篡改。
如下图所示,标准 JWT 令牌的三个部分由点号 (.) 分隔,并采用 Base64 URL 安全编码。接下来我们将深入了解构成有效 JWT 的每个部分。如果你已经熟悉 JWT 的基本结构,可以直接跳到漏洞利用部分。
解构 JSON Web Token (JWT)
当载荷部分被加密时,我们称之为 JSON Web Encryption (JWE)。
头部 (Header)
头部包含用于指导 JWT 库进行验证、解密和签名的元数据。这些元数据包括签名算法、令牌类型、标识符 (如 kid) 等相关信息。
下图展示了 JWT 头部中可能包含的所有元数据属性。在本文后续部分,我们将介绍错误配置和输入验证缺失是如何导致 JWT 漏洞的。
理解 JSON Web Token (JWT) 头部结构
载荷 (Payload)
载荷包含实际的 JWT 声明以及应用程序所需的数据。最佳实践建议:除非经过加密 (即使用 JWE),否则不要在载荷中包含敏感数据 (如账单信息、电子邮件或其他个人身份信息 PII)。需要注意的是,在漏洞赏金项目中,仅报告未遵循最佳实践但未导致实际漏洞的问题,通常会被标记为信息性发现而非有效漏洞。
签名 (Signature)
签名用于验证令牌的完整性,确保在没有签名密钥的情况下无法篡改数据。在本文后续部分,我们将介绍忽视 JWT 实现标准是如何引入安全漏洞的。
至此,我们已经介绍了 JSON Web Tokens 的基本概念,以及 JSON Web Signature (JWS) 和 JSON Web Encryption (JWE) 之间的区别。接下来让我们进入 JWT 漏洞利用部分。
利用 JWT 漏洞
JWT 漏洞通常源于实现过程中的错误配置和输入验证不当。下面我们将介绍 7 种 JWT 漏洞测试方法。
1. 允许 None 算法
如本文前面 JWT 解构部分所述,JWT 头部包含用于解析令牌的所有元数据。在某些情况下,开发者会启用 none 算法支持,以便发放和使用未签名的令牌——通常用于测试目的或支持非敏感场景 (如公开分享链接)。
然而,如果在生产环境中未正确处理这种情况,任何人都可以篡改 JWT,包括修改声明以提升应用权限,或发起注入攻击。
假设以下 JSON Web Token 用于某个会计平台:
会话处理 JWT 示例
任何用户都可以:
- 对头部和载荷进行 Base64 解码
- 将头部中的
alg属性设置为none - 篡改声明——在本例中,将
role属性改为owner,将organizationId改为目标组织的 ID - 最后,完全移除签名部分,但保留末尾的点号
这样就可以冒充任意组织的所有者:
None 算法 JWT 攻击
这并非开发者在签名验证中唯一容易出错的地方。接下来我们看看更多的 JWT 漏洞利用示例。
2. 缺少签名验证
与前面的错误配置类似,某些情况下开发者会在未提供签名时完全跳过签名验证。这通常是为了方便测试应用的认证功能,或为未签名令牌提供支持 (如书签分享链接或应用内配置预设)。
同样地,当令牌解析不正确时,攻击者可以篡改声明并冒充其他用户以提升应用权限,或在服务器错误处理用户输入时发起注入攻击。
要测试这种漏洞,需要完全移除签名,篡改 JWT 声明,然后只向服务器发送头部和载荷。
缺少签名验证的 JWT 攻击
由于此漏洞源于令牌解析不当,你可能需要尝试修改 alg头部属性,传入非预期值如 none。
3. JWT 算法混淆攻击
如果之前所有将算法设置为 none的尝试都失败了,强烈建议进行进一步测试,因为服务器可能支持其他算法,这可能导致 JWT 密钥混淆攻击。
JWT 算法 (或密钥) 混淆攻击源于开发者对 JWT 算法处理不当,导致服务器支持默认算法之外的其他算法。这使攻击者可以为令牌签名指定其他算法,例如 HS256——该算法通常使用公钥作为签名密钥。
实际利用中,通常需要将 JWT 头部的 alg属性改为 HS256,篡改载荷,然后使用服务器的公钥对令牌进行签名。你需要找到服务器使用的确切公钥,这通常可以在服务器上获取到。
4. JWK 欺骗攻击
在某些场景中,你会发现 JWT 解析库存在缺陷,允许攻击者在令牌中包含自己的密钥对。这听起来似乎不太可能,但 CVE-2018-0114 就是一个典型案例——该漏洞源于类型混淆,攻击者只需在令牌中包含任意密钥对即可伪造令牌。让我们深入分析这个 CVE,了解这一 JWT 漏洞是如何产生的。
解析缺陷
node-jose库允许任何未认证的攻击者对令牌进行签名,只要在头部中指定了 jwk属性。回顾之前的表格,我们可以看到 jwk属性包含多个嵌套属性,用于指示解析库验证签名:
理解 JSON Web Token (JWT) 头部结构
构造如下头部的 JSON Web Token,实际上会让解析库使用攻击者提供的密钥对来验证签名,从而允许任何人伪造令牌内容:
{
"alg": "RS256",
"typ": "JWT",
"kid": "example-key-id",
"jwk": {
"kty": "RSA",
"kid": "example-key-id",
"use": "sig",
"n": "uAPuSn1MG6nFYKjiIcfke-nyMfsZM_Wrea7wlv1l553UrUM8P9VjZ0kTKYX3iyWLDXgyokLsZtqicE5q3c71cQ",
"e": "AQAB"
}
}
你需要生成自己的密钥对,并将其转换为 n和 e属性所需的格式。
该缺陷源于对 jwk属性的错误处理——解析库信任了攻击者指定的任何密钥对。
除了 JWT 库的错误配置外,还存在其他安全疏忽,例如 JWT 头部属性中的注入攻击以及使用弱密钥等问题。接下来我们看看更多 JWT 漏洞利用的示例。
5. 密钥 ID (kid) 注入攻击
如前所述,当头部参数处理不当时会产生安全漏洞。在某些情况下,你会注意到 JSON Web Token 的头部中引用了密钥 ID (kid)。该属性表示密钥文件的位置,实际上是指示解析库从服务器文件系统的何处获取签名密钥。
部分目标系统使用多个密钥,因此会用 kid参数来标识签名所用的密钥对。在这种情况下,我们可以根据参数的处理方式测试路径遍历、SSRF 和注入攻击。下面来看几个示例。
通过 JWT kid 属性进行路径遍历攻击
考虑以下存在漏洞的代码示例:
通过 JWT kid 注入实现路径遍历
我们可以清楚地看到,在第 15 行,应用程序读取 kid参数并直接拼接到文件读取函数中,没有进行任何验证。在示例代码中,它原本指向一个不可猜测的签名密钥。然而,由于我们可以进行路径遍历,就能让应用程序从其他目录获取签名密钥,只要该文件存在于服务器文件系统中。我们的目标是找到一个返回可预测密钥内容的文件,这样就可以使用相同的密钥在本地复制 JWT 签名过程。
实际利用 JWT kid 路径遍历的步骤如下:
- 根据需要修改声明部分,并将 JWT 头部的
kid属性设置为服务器文件系统上的任意文件 (如/dev/null) - 使用相同的密钥文件 (如
/dev/null) 对伪造的 JWT 进行签名 - 将伪造的 JSON Web Token 发送到处理 JWT 的端点
- 存在漏洞的应用程序将读取伪造的 JWT,定位
kid属性指定的密钥,最终使用获取到的密钥验证签名
{
"alg": "RS256",
"kid": "../../../dev/null"
}
这种攻击本质上允许我们伪造 JWT 并使用服务器文件系统上的任意文件作为密钥进行签名。接下来看一个类似的示例,其中签名密钥从数据库中获取。
通过 JWT kid 属性进行 SQL 注入攻击
与前面的情况类似,如果目标系统在从数据库加载签名密钥时使用了 kid属性,我们也可以测试 SQL 注入,甚至 NoSQL 注入。
考虑以下存在漏洞的代码片段:
通过 JWT kid 注入实现 SQL 注入
在第 11 行可以看到,kid属性在执行前被直接拼接到未经预处理的 SQL 语句中。在这种情况下,只需跳出原有语境并注入可预测的 payload 字符串,就能将原始签名密钥替换为我们控制的字符串值。
{
"alg": "RS256",
"kid": "example-key' OR UNION SELECT 'intigriti'; --"
}
按照之前相同的概念验证步骤,我们可以伪造带有恶意声明的 JWT、从数据库提取敏感数据,在特定条件下甚至可以在目标服务器上执行代码。
6. 暴力破解常见/弱 JWT 密钥
使用常见或弱密钥也会导致令牌被轻易伪造。借助 John The Ripper 或 JWT_tool 等工具,我们可以轻松暴力破解用于签名和验证令牌的弱密钥。需要注意的是,这种攻击仅适用于使用对称密钥 (secret) 签名的令牌。RSA 签名的 JWS 和 JWE 通常需要完整的密钥对 (包括私钥),因此猜测攻击几乎不可能成功。
以下示例展示了如何使用单个 JWT 令牌和包含可能匹配项的字典文件来破解密钥:
$ john --wordlist=/path/to/wordlist.txt jwt.txt # jwt.txt 文件包含你的 JWT 令牌
7. 枚举硬编码的 JWT 密钥
密钥有时会被意外提交到公共代码仓库、JavaScript 文件或其他配置文件中,其中可能包含用于签名和验证 JWT 的密钥。
建议充分利用公开信息和侦察技术,如 JavaScript 文件枚举、GitHub 高级搜索 和 Google Dorking 来发现意外泄露的硬编码密钥。
提示
在客户端发现 JWT 密钥,强烈暗示该 JWT 是在客户端进行签名和验证的。请务必验证密钥的实际保密性。
总结
JWT 漏洞可能源于各种错误配置、未遵循最佳实践以及解析缺陷。本文介绍了开发者在实现 JWT 过程中因疏忽而可能产生的多种安全问题。
Exploiting JWT vulnerabilities: A complete guide
免责声明:本博客文章仅用于教育和研究目的。提供的所有技术和代码示例旨在帮助防御者理解攻击手法并提高安全态势。请勿使用此信息访问或干扰您不拥有或没有明确测试权限的系统。未经授权的使用可能违反法律和道德准则。作者对因应用所讨论概念而导致的任何误用或损害不承担任何责任。
免责声明:
本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。
任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。
本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我。
本文转载自:securitainment AYOUB《JWT 漏洞利用完全指南》