教程
🛡️

网络安全

Web 安全、渗透测试、密码学与安全合规的系统化教程。

JWT 渗透:伪造那张身份认证令牌

讲清 JWT 令牌结构与原理,覆盖空加密验证攻击、字典爆破弱密钥、认证键值逻辑缺陷等,并给出强密钥、算法锁定等防御。

· 更新于 2026-07-18 阅读量 --

JWT(JSON Web Token) 是现代 Web 常用的无状态认证令牌:登录后服务器发你一张”签过名的 JSON”,之后每次请求带上它证明身份。问题来了——如果这张”签名”能被绕过或伪造,你就能伪装成任意用户(包括管理员)

以下仅在授权靶场(如 JWT 专项靶场、DVWA 相关关卡)练习;对他人系统伪造 JWT 属违法。

JWT 长什么样

一个 JWT 是三段用 . 分隔的 base64 字符串:

eyJhbGciOiJIUzI1NiJ9.eyJ1c2VyIjoiYWxpY2UiLCJyb2xlIjoidXNlciJ9.xxxxx
└─ Header ─┘ └──── Payload(数据)────┘ └─ Signature(签名)─┘
  • Header:算法等信息,如 {"alg":"HS256","typ":"JWT"}
  • Payload:存放数据,如 {"user":"alice","role":"user"}
  • Signature:用密钥对前两段签名,证明”这令牌是我发的、没被改”。

服务器收到 JWT 后,重新算一遍签名,和传来的签名比对;一致才信任 payload 里的身份。

攻击一:算法置空(alg: none)

早期一些库支持 alg: none——“不签名”。攻击者在 Header 把算法改成 none,并删掉签名段

Header: {"alg":"none","typ":"JWT"}
Payload: {"user":"admin","role":"admin"}
→ token = base64(header).base64(payload).   (签名留空)

若服务器那端也接受了 none,它就不校验签名,直接信任 payload——于是你把自己伪造成了 admin。现代库大多已禁 none,但老代码/错误配置仍有。

攻击二:HS256 用公钥当密钥(密钥混淆)

JWT 有两类算法:

  • 对称(HS256):服务器用同一个密钥签名和验签;
  • 非对称(RS256):服务器用私钥签名,把公钥公开给客户端验签。

如果服务器配置用 RS256(验签用公钥),但代码实际用了 HS256 验签(用”密钥”验签),攻击者就能拿公开的公钥当 HS256 的”密钥”来伪造签名——因为公钥本来就能拿到!于是用公钥对恶意 payload 做 HS256 签名,服务器用”公钥当密钥”验签竟通过了。

防御:服务端锁定算法,不允许客户端在 Header 里指定算法(alg 固定写死在服务端)。

攻击三:爆破弱密钥

HS256 的密钥如果弱(如 secret123456),攻击者可以离线爆破:拿已知 payload 的 JWT,用字典逐个密钥算签名,撞上就得到密钥,之后能伪造任意 token。

工具:johnhashcat 配合 JWT 字典,或 jwt-cracker 直接跑。防御:用长随机密钥(如 32 字节以上随机串),弱密钥必被破。

攻击四:Payload 被信任但未校验

即使签名没问题,若服务器没校验过期时间(exp)、或信任了 payload 里”角色”字段却没和数据库核对,攻击者可改 role 或改 exp 为很久以后。所以服务端不能只信 JWT 内容,关键权限仍要查库。

实战流程(靶场)

  1. 抓登录后的 JWT,用 jwt.iojq 解码看 payload(user/role/exp);
  2. 尝试把 algnone、删签名,看服务器接不接收;
  3. 若 RS256,尝试用公钥按 HS256 重签;
  4. jwt-cracker 爆破弱密钥;
  5. role/user 为 admin,重签后重放请求,看是否进入管理员区。

防御:把令牌管严

1. 强密钥 + 不泄露

HS256 用足够长、随机、保密的密钥;RS256 私钥严格保密,公钥只用于验签且服务端算法锁定。

2. 服务端锁定算法

alg 写死在服务端配置,绝不信任 token Header 里的 alg。这是防 alg:none 和密钥混淆的关键。

3. 校验完整声明

验签同时校验 exp(过期)、iss(签发者)、aud(受众)、nbf(生效时间)等,过期令牌直接拒。

4. 关键权限查库

不要只信 JWT 里的 role,涉及敏感操作时后端按用户 ID 查库确认权限(防 payload 被改/被信任过度)。

5. 令牌短期有效 + 可吊销

access token 设短过期(如 15 分钟),配合 refresh token;提供吊销机制(黑名单/版本号),丢失可作废。

6. 用成熟库

别自己实现 JWT 逻辑,用经过审计的官方库,并保持更新。

更多实战案例:JWT 的三种翻车姿势

第一种:算法混淆。服务器用 RS256(非对称)验签,但代码里若把算法字段交给了 token 自己声明,攻击者可把 alg 改成 HS256,然后用服务器的公钥当 HMAC 密钥签名——因为公钥是公开的,于是伪造出合法 token。修复就是硬编码算法、不读 token 里的 alg。

第二种:密钥太弱。HS256 的密钥若是 secret123 这类弱口令,攻击者拿 JWT 破解工具(如 john 配合 jwt.txt 字典)几秒爆破出来,之后想签什么签什么,把 admin:true 塞进去。所以密钥必须足够随机且长。

第三种:none 算法。老库允许 alg:none 表示”不校验签名”,攻击者在 token 里把 alg 改成 none、删掉签名部分,服务器若没拦就直接放行。现代库已默认禁止,但老系统仍可能中招。

更多实战案例:改 claim 提权

假设正常 token 的 payload 是 {"user":"alice","role":"user"},攻击者解码后把 role 改成 admin 再重新签名(若密钥已知或算法可降维),服务器读到 role=admin 就给了管理员权限。还有把 sub(用户标识)改成别人的 id,实现越权访问他人数据。

常见坑

  1. 把 alg 交给客户端决定:必须服务端硬编码。
  2. 密钥写死在代码里还很简单:弱密钥等于没加密。
  3. 只校验过期不校验签名完整性:签名被篡改仍放行。
  4. 把敏感信息塞进 payload:payload 只是 base64,任何人都能解码,别放密码。

进阶:修复

服务端固定算法(白名单且硬编码);使用强随机密钥并妥善保管;禁止 none;校验时同时验签和验过期;不在 payload 放机密;必要时用短期 access token + 刷新机制。

小测验

  • 问题1:RS256 被降为 HS256 利用的是什么?答案:用公开的公钥当 HMAC 密钥签名。
  • 问题2:弱密钥最大风险?答案:被字典爆破,进而任意伪造 token。
  • 问题3:payload 里的数据任何人能读到吗?答案:能,base64 可解码,别放敏感信息。

更多实战案例:用工具实操 JWT 破解

拿到一个 token,先用 jwt.io 或命令行 jq 解码看 header 和 payload,确认算法、过期时间、是否有 admin/role 等 claim。若算法是 HS256 且你怀疑弱密钥,用 john 配合 jwt.txt 字典爆破:john --wordlist=rockyou.txt hash.txt,几秒到几分钟可能出结果。拿到密钥后,用任意 JWT 库重新签名一个 role:admin 的 token。若是 RS256→HS256 混淆,用服务器公钥(常可从 /.well-known/jwks.json 或证书拿到)当 HMAC 密钥签名即可。

更多实战案例:none 算法与时间攻击

老库允许 alg:none,攻击者把 header 改成 {"alg":"none"}、删掉签名部分(或留空),服务器若没拦就直接放行——这是最省事的伪造。还有”密钥暴力枚举”之外的”密钥混淆”:某些实现把对称密钥和非对称混用。另外注意 exp(过期)、nbf(生效前)时间字段,若服务器不校验,过期 token 仍可用。

更多实战案例:改 claim 提权的完整链路

正常 token:{"sub":"alice","role":"user","exp":...}。攻击者解码后改 roleadminsubadmin 账号,重新签名(若密钥已知或算法可降维),带着新 token 访问需要管理员权限的接口,服务器读到 role=admin 就放行。还有把 sub 改成别人的用户 id 实现越权访问他人数据。这类利用依赖”签名可被伪造”,根因在密钥或算法配置。

常见坑(补充)

  1. 把 alg 交给客户端决定:必须服务端硬编码算法白名单。
  2. 密钥写死在代码里还很简单:弱密钥等于没加密,要强随机长密钥。
  3. 只校验过期不校验签名完整性:签名被篡改仍放行。
  4. 把敏感信息塞进 payload:payload 只是 base64,任何人能解码,别放密码。

进阶(补充):修复要点

服务端固定算法(白名单且硬编码);使用强随机密钥并妥善保管,不进代码仓库;禁止 none;校验时同时验签和验过期;不在 payload 放机密;用短期 access token + 刷新机制,降低泄露影响;必要时对敏感操作再加一次凭证确认。

小测验(补充)

  • 问题1:RS256 被降为 HS256 利用什么?答案:用公开的公钥当 HMAC 密钥签名。
  • 问题2:弱密钥最大风险?答案:被字典爆破,进而任意伪造 token。
  • 问题3:payload 里的数据任何人能读到吗?答案:能,base64 可解码,别放敏感信息。

更多实战案例:JWT 与 OAuth 的关系

JWT 常作为 OAuth2 的 access token 或 OpenID Connect 的 id_token。理解这点有助于测试:拿到 token 后看它是不是 JWT(三段 base64),解出来看 issuer、audience、scope 是否正确校验。很多漏洞出在”不校验 audience”——攻击者用一个服务的 token 去访问另一个服务;或”不校验 issuer”——伪造签发方。这些都属于”签名之外还要校验声明”的疏忽。

常见坑(终补)

  1. 把 alg 交给客户端决定:必须服务端硬编码算法白名单。
  2. 密钥写死在代码里还很简单:弱密钥等于没加密。
  3. 只校验过期不校验签名完整性:签名被篡改仍放行。
  4. 把敏感信息塞进 payload:payload 只是 base64,任何人能解码。

进阶(终补):修复要点

服务端固定算法(白名单且硬编码);强随机密钥妥善保管;禁止 none;校验时同时验签、验过期、验 audience、验 issuer;不在 payload 放机密;短期 access token + 刷新机制;敏感操作再加凭证确认。

小测验(终补)

  • 问题1:RS256 降为 HS256 利用什么?答案:用公开公钥当 HMAC 密钥签名。
  • 问题2:弱密钥最大风险?答案:被字典爆破,任意伪造 token。
  • 问题3:payload 数据任何人能读到吗?答案:能,base64 可解码,别放敏感信息。

实战要点:JWT 安全自查清单

上线前自查:算法是否硬编码(不读 token 里的 alg)?是否用强随机密钥且未进代码仓库?是否禁止 none?是否同时校验签名、过期、audience、issuer?payload 是否不含敏感信息?是否用短期 token + 刷新?把这六条做成上线检查项,能挡掉绝大多数 JWT 漏洞。密钥管理用环境变量或密钥中心,定期轮换。

易错提醒

开发者常犯:把算法交给客户端(图省事)、密钥写成 secret(图好记)、把用户角色塞 payload 又相信它没被改。记住 payload 是”明文便利贴”,任何重要判断都要服务端独立校验,不能因为”token 是我发的”就完全信任里面的声明。签名只保证”没被篡改”,不保证”声明合理”。

自测

  • RS256 降为 HS256 利用什么?答:用公开公钥当 HMAC 密钥签名。
  • 弱密钥最大风险?答:被字典爆破,任意伪造 token。
  • payload 数据任何人能读到吗?答:能,base64 可解码,别放敏感信息。

延伸思考:JWT 与 Session 的取舍

传统 Session 把状态存在服务端,JWT 把状态存在 token 里。JWT 适合无状态、跨服务(如微服务、移动端),但一旦密钥泄露或算法配置错,影响面大且难吊销(除非短时效+黑名单)。Session 吊销容易但需服务端存储。选型看场景:内部可信服务可用 Session;对外多端可用 JWT 但务必短时效、强密钥、校验全声明。没有绝对好坏,只有是否用对。

一句话自测

  • 为什么说 payload 像”明文便利贴”?答:base64 可解码,任何人都能读,别放机密。
  • 修复 JWT 最不可少的一步?答:硬编码算法白名单,不读 token 里的 alg。

这一篇你该记住的

JWT 是”签名过的 JSON 令牌”,三段 header.payload.signature。攻击:alg:none 置空签名、RS256/HS256 密钥混淆(用公钥当密钥重签)、爆破弱密钥、信任未校验的 payload。防御:强随机密钥、服务端锁定算法(不信任 Header alg)、校验 exp/iss/aud、关键权限查库、短期有效+可吊销、用成熟库。

JWT 是”认证令牌的伪造”。下一篇 中间件漏洞 跳出网站代码,看 Web 容器/中间件自己漏了会怎样——Apache、Nginx、Tomcat、WebLogic 各有经典坑。