读 JWT:解码能看到什么,又能证明什么
读 JWT:解码能看到什么,又能证明什么
JWT 是三段 base64url,解码其中一段并不能说明它是不是真的。怎么读懂各项声明,以及令牌为什么会在 URL 里坏掉。
原创流程图
读 JWT:解码能看到什么,又能证明什么
理解原理
检查后再进入下一步
检查边界
检查后再进入下一步
使用工具
检查后再进入下一步
标准签名 JWT 的头部和载荷是 base64url 文本,不是密文。任何拿到令牌的人——从 URL、日志文件、浏览器开发者工具、Referer 头——都能不用任何密钥读出里面的每一项声明。签名保护的是完整性而不是机密性:它能证明令牌没被篡改,但完全不能说明谁有权看它。凡是你不愿意写进日志的内容,就不要放进载荷。
解析工具做的事是拆分令牌并对两段做 base64 解码。只要输入格式正确,这个操作永远成功,包括对一个手工伪造的令牌。验签是另一步:用密钥或公钥重新计算签名并比对。调试工具给你显示了一份整洁、看起来合理的载荷,并不能证明这个令牌是真的。「因为解码正常所以相信这项声明」,是一整类认证漏洞的根源。
JWT 用的是 URL 安全字母表:减号和下划线替代加号和斜杠,末尾的等号填充被去掉。把 JWT 粘进标准 base64 解码器,往往会因为填充报错,或者在被替换的字符上把字节弄乱。这也解释了为什么令牌在一个系统里正常、到下一个系统就坏了——中间有人「好心地」修正了编码,或者某个 URL 解码器把合法字符变成了别的东西。
exp 是过期时间,iat 是签发时间,nbf 是生效时间。三者都是以秒为单位的 Unix 时间戳,不是毫秒——这是令牌看起来在 1970 年或遥远未来过期的常见原因。由于签发方和验证方的时钟会漂移,实现通常允许几秒的容差。如果令牌总是在刚签发时偶发失败,那么「nbf 声明 + 签发服务器时钟偏快」是首选嫌疑。
放在查询串里的 JWT 会出现在浏览器历史、服务器访问日志,以及发给第三方的 Referer 头里。它同时也很脆弱:令牌很长,部分代理和老客户端会截断超过几千字符的 URL,而被一层编码一次、又被另一层再编码一次的令牌,到达时就已经损坏。如果同一个令牌放在请求头里能用、放进链接就不行,先逐字符比对这两个字符串——答案通常是二次编码。
头部声明了签名算法,而幼稚的验证方会读它并照做。由此产生两种经典攻击:一种把 alg 设成 none 并直接去掉签名;另一种把 RS256 令牌换成 HS256,让服务器把公钥当作 HMAC 密钥来验签——而公钥本来就是公开的。两者的修复方式相同:验证方事先决定接受哪种算法,拒绝其他一切,而不是听从它正在检查的那个令牌的指挥。
令牌异常时,先解码头部和载荷,记下 alg、exp、iat、nbf 以及 audience 或 issuer 声明。确认时钟单位是秒。把正常路径和失败路径的原始令牌字符串逐字符比对。只有在理解载荷之后,才用预期算法和密钥做验签。如果头部里的算法不在允许列表上,直接拒绝,不要尝试验证。
常见问题
标准签名 JWT 不是。载荷是 base64url 文本,任何拿到令牌的人都能读。要加密需要单独的 JWE 格式。
不可以。解码对伪造令牌同样会成功。只有用正确密钥验签通过,才建立了信任。
通常是编码问题。JWT 用 base64url,URL 解码器或二次编码层可能改动了减号、下划线或填充字符。
先看 exp 是不是以秒为单位。毫秒时间戳会大一千倍,而秒值被当成毫秒解析会落在 1970 年。
相关工具
JWT 生成器
直接在浏览器中生成 HS256、HS384、HS512 的 JWT,适合测试和本地工作流。
JWT 解析工具
在本地解析 JWT 的头部和载荷,查看 claims、过期时间和原始 Base64URL 段。
URL 解析器
把 URL 拆成协议、主机、路径、查询和哈希等字段,并清楚查看所有参数。
查询字符串生成器
从键值对生成干净的查询字符串,并立即预览最终 URL 片段。
JSON 压缩
直接在浏览器中压缩 JSON,去掉多余空白后再交给其他系统。
URL 编码解码
编码/解码 URL 组件或完整 URL,并解析查询参数。
Slug 生成器
把标题或短语转换成干净的 URL Slug,支持分隔符和大小写选项。