借pac4j-jwt 认证绕过漏洞重温JWT
当网站使用了 pac4j-jwt并且以jwe形式作为身份权限凭证,则可能会有身份令牌任意伪造风险
前言
2026 年 3 月,安全研究团队 CodeAnt AI 在 Java 认证库 pac4j-jwt 中发现了一个 CVSS 10.0(满分)的严重漏洞 CVE-2026-29000。攻击者仅需服务器的 RSA 公钥,即可伪造任意身份的 JWT 令牌,完全绕过认证。
在分析这个漏洞之前,先来搞清楚几个核心概念。
一、JWT 家族
1.1 JWT(JSON Web Token)
JWT 是一种紧凑的、URL 安全的令牌格式,用于在各方之间传递 JSON 格式的声明(Claims)。它是一个统称,涵盖了下面三种具体类型。
一个典型的 JWT Claims 长这样:
1 | { |
这些 Claims 描述了”这个人是谁、有什么权限、令牌何时过期”等信息,是认证决策的核心依据。
JWT 规范(RFC 7519)定义了三种令牌类型:
| 类型 | 全称 | 安全属性 | 结构 |
|---|---|---|---|
| JWS | JSON Web Signature | 签名(完整性 + 真实性) | Header.Payload.Signature |
| JWE | JSON Web Encryption | 加密(机密性) | Header.EncKey.IV.Ciphertext.Tag |
| PlainJWT | Unsecured JWT | 无 | Header.Payload.(签名为空) |
1.2 JWS(JSON Web Signature)—— 签名令牌
JWS 是最常见的 JWT 形式。它由三部分用 . 拼接而成:
1 | {Header}.{Payload}.{Signature} |
- Header:声明签名算法(如
RS256) - Payload:Base64Url 编码的 Claims
- Signature:对
Header.Payload的数字签名
JWS 特点:可读但不可篡改
类比:JWS 就像一封盖了蜡封印章的信。任何人都可以读信的内容(Payload 只是编码,不是加密),但蜡封能证明这封信确实来自声称的发件人,而且内容没有被篡改过。
1 | 签名流程:Claims ──用私钥签名──→ JWS |
私钥签名,公钥验签。只有持有私钥的服务器能生成合法签名,但任何持有公钥的一方都可以验证签名是否有效。
1.3 JWE(JSON Web Encryption)—— 加密令牌
JWE 对令牌内容进行加密,由五部分用 . 拼接而成:
1 | {Header}.{加密密钥}.{初始向量}.{密文}.{认证标签} |
- Header:声明密钥加密算法(如
RSA-OAEP-256)和内容加密算法(如A256GCM) - Encrypted Key:用接收方公钥加密的对称密钥(实际加密内容用的是这个对称密钥,即”混合加密”)
- IV(Initialization Vector):初始化向量,配合对称加密算法使用,保证相同明文每次加密产生不同密文
- Ciphertext:用对称密钥加密后的实际内容(即被保护的 JWS 或 Claims)
- Authentication Tag:认证标签,用于验证密文在传输中未被篡改(由 GCM 等 AEAD 模式自动生成)
JWE 特点:不可读
类比:JWE 就像把信装进一个上锁的保险箱。只有持有钥匙的人(服务器私钥)才能打开并阅读内容,传输过程中任何第三方都无法窥探。
1 | 加密流程:内容 ──用公钥加密──→ JWE |
公钥加密,私钥解密。任何人都可以用公钥加密数据(公钥本就公开),但只有持有私钥的服务器能解密。
关键:加密成功 ≠ 来源可信。 因为公钥是公开的,攻击者同样可以用公钥加密一段恶意内容,服务器照样能成功解密。加密只保证”只有服务器能读”,不保证”这是服务器签发的”。
1.4 PlainJWT(Unsecured JWT)—— 无保护令牌
PlainJWT 是 JWT 规范中定义的第三种类型,也叫”不安全 JWT”。它的 Header 中算法字段为 "alg": "none":
1 | { |
结构上,它和 JWS 类似,但签名部分为空:
1 | {Header}.{Payload}. |
PlainJWT 不提供任何安全保障。 它的设计初衷包括:
- 已有外部安全保障时避免重复保护:例如在 TLS 加密的内部微服务通信中,签名被视为多余开销
- 作为结构化数据载体:某些场景只需要 JWT 的标准化格式来封装非敏感数据
- 开发调试:在测试环境中快速生成令牌,省去配置密钥的步骤
安全社区对 alg: none 一直持高度警惕态度。直接发送 PlainJWT 作为认证令牌是一种经典攻击手法(2015 年就已被广泛讨论),主流 JWT 库大多已默认拒绝此类令牌。
二、标准做法:JWE + JWS 双层保护
在 pac4j-jwt 的生产部署中,通常将 JWS 和 JWE 组合使用,实现双层保护:

生成令牌的流程:
1 | 1. 构建 Claims(身份信息) |
验证令牌的流程:
1 | 1. 客户端发送 JWE 令牌 |
类比:先给信件盖蜡封印章(签名),再锁进保险箱(加密)。收件人先开保险箱,再验蜡封,两步都通过才信任信件内容。
两层保护缺一不可:
| 场景 | 只有加密 | 只有签名 | 两者都有 |
|---|---|---|---|
| 第三方能读取令牌内容? | 不能 | 能 | 不能 |
| 攻击者能伪造令牌? | 能 | 不能 | 不能 |
| 攻击者能篡改 Claims? | 能 | 不能 | 不能 |
三、CVE-2026-29000
3.1 漏洞代码分析
问题出在 pac4j 的 JwtAuthenticator.java 中,以下是validate函数源码:
1 | public void validate(final TokenCredentials credentials, final WebContext context) { |
toSignedJWT() 是 Nimbus JOSE+JWT 库的方法:
- 如果内部 payload 是 JWS(签名令牌)→ 返回解析后的 JWS 对象
- 如果内部 payload 是 PlainJWT(无签名令牌)→ 返回
null
当 signedJWT 为 null 时:
if (signedJWT != null) { jwt = signedJWT; }被跳过,jwt保持之前的值- 整个签名验证块
if (signedJWT != null) { ... }被跳过 createJwtProfile()照常执行,未经验证的 Claims 被直接信任
3.2 漏洞根因
org.pac4j.jwt.credentials.authenticator.JwtAuthenticator的validate方法在处理jwe逻辑中,jwe加密的不是jws而是PlainJWT时没有对PlainJWT攻击做拦截判断,代码层面 没有if (signedJWT == null)的处理逻辑
四、漏洞利用:五步冒充管理员
第1步:获取服务器 RSA 公钥
公钥本就是设计为公开的,通常可通过以下途径获取:
- JWKS 端点:
/.well-known/jwks.json - 应用文档或配置文件
- 公开的代码仓库
第2步:构造恶意 Claims
1 | JWTClaimsSet maliciousClaims = new JWTClaimsSet.Builder() |
第3步:创建 PlainJWT(关键)
不签名,直接用 PlainJWT 包装 Claims:
1 | PlainJWT innerJwt = new PlainJWT(maliciousClaims); |
这会使 Nimbus 的 toSignedJWT() 返回 null,从而触发 pac4j 中的逻辑缺陷。
第4步:用公钥加密成 JWE
1 | JWEObject jweObject = new JWEObject( |
从外部看,这个令牌和合法 JWE 令牌完全一样——格式正确、加密有效。
第5步:发送令牌
1 | Authorization: Bearer eyJhbGciOiJSU0EtT0FFUC0yNTYi... |
服务器端处理过程:
1 | JWE 解密成功 (令牌用了正确的公钥加密) |
不需要私钥,不需要共享密钥,不需要暴力破解。只需要一把公钥。
五、影响
同时满足以下所有条件的项目受影响:
- 使用了 pac4j-jwt
- 配置了 JWE 加密(
RSAEncryptionConfiguration) - 同时配置了签名验证(
RSASignatureConfiguration) - 通过
JwtAuthenticator进行认证
不受影响的情况:
- 只使用 JWS 签名,不使用 JWE 加密
- 未使用 pac4j-jwt
受影响版本与修复
| 版本线 | 受影响版本 | 修复版本 |
|---|---|---|
| 4.x | < 4.5.9 | 4.5.9+ |
| 5.x | < 5.7.9 | 5.7.9+ |
| 6.x | < 6.3.3 | 6.3.3+ |
参考链接
- Title: 借pac4j-jwt 认证绕过漏洞重温JWT
- Author: mapl3miss
- Created at : 2026-03-20 19:22:34
- Updated at : 2026-03-24 20:28:48
- Link: https://redefine.ohevan.com/2026/03/20/借pac4j-jwt-认证绕过漏洞重温JWT/
- License: This work is licensed under CC BY-NC-SA 4.0.