实战指南 7 分钟阅读

JWT 详解:结构、校验,以及导致真实泄露事故的那些错误

JWT 三段结构里到底装了什么?为什么解码不等于校验?盘点造成真实安全事件的六大实现错误。

JSON Web Token 在认证体系里无处不在,但它表面简单、内里锋利:有的 token 解码正常却证明不了任何事;载荷谁都能读;配置错误直接酿成过去十年最著名的几起 API 泄露事故。

三段结构到底是什么

JWT 是三段 Base64URL 编码的字符串用点连接:

eyJhbGciOiJIUzI1NiJ9  .  eyJzdWIiOiIxMjMifQ  .  SflKxwRJSme...
       header                payload              signature
  • Header —— JSON 对象,声明算法(alg)和类型。注意 HS256 是 HMAC-SHA256(对称),RS256/ES256 是非对称签名。
  • Payload —— 声明(claims):sub(主体)、exp(过期时间)、iss(签发者)、aud(受众),加上你的业务自定义内容。
  • Signature —— 证明”这两段内容确实由密钥持有者签署、之后未被修改”。

把任意 token 粘进 JWT 解码器,三段内容都能读出来——完全在浏览器本地完成。

解码不等于校验(酿成泄露的陷阱所在)

解码只是 Base64URL 加 JSON 解析,人人可做,“读载荷”本来就是解码器的用途。校验是另一回事:用收到的 header 和 payload 重算签名比对是否匹配,再逐项验证声明(expnbfissaud)。经典 JWT 漏洞全部来自跳过了其中某一步:

  1. alg: none —— 老库在 header 声明”无签名”时直接放行。服务端必须固定自己期望的算法。
  2. 算法混淆 —— 配了 RS256 的服务端如果接受自称 HS256 的 token,攻击者就能把”公钥”当 HMAC 密钥来伪造签名。算法在服务端写死,绝不信任 header 里的 alg
  3. 不检查过期 —— 载荷没有 exp、或校验方忽略它,就会产出”永久有效”的 token。强制要求 exp 并在服务端拒绝过期 token。
  4. 不检查受众和签发者 —— 给 A 服务签发的 token 原样重放给 B 服务也能通过,只要两边都不看 aud/iss

JWT 保护什么、不保护什么

载荷只是编码、不是加密——任何拿到 token 的人都能读出全部声明,解码器展示的就是攻击者看到的。绝不要把密码、API 密钥、个人敏感信息放进 payload。签名真正保证的是:声明出自密钥持有者之手、签发后未被篡改。两个直接推论:

  • 把 JWT 当凭证保管:抗 XSS 角度 HttpOnly Cookie 优于 localStorage。
  • 短效 access token(分钟级)+ refresh 轮换,优于长达一周的大 token——exp 只有够短才有意义。

经得起评审的校验清单

后端接受 token 时应按序执行:用固定算法和可信密钥验签 → 用当前时间检查 exp/nbf(留少量时钟偏差余量)→ 比对 iss/aud 与预期值 → 全部通过才信任声明。客户端侧解码读 exp 来决定何时刷新没问题——但客户端的解码永远代替不了服务端校验。

token 被拒了?用 JWT 解码器解出载荷,逐项和服务端配置比对——iss/aud 不匹配和时钟偏差贡献了大多数”token 无效”工单。开发中需要签发 HMAC 签名或验证 Webhook 签名时,HMAC 计算器可以在本地算出参考值。