资讯详情

JWT 到底装了什么?贴一个 token 把 3 段拆开,实测 alg=none 的坑

📅 2026/10/15 8:00:56 | 华诺云谱 👁 阅读
JWT 到底装了什么?贴一个 token 把 3 段拆开,实测 alg=none 的坑
一个 224 字符的 JWT 贴进去页面当场把它拆成三段27 字节的 header、107 字节的 payload、32 字节的签名。填上密钥点一下按钮页面自己算出的 HMAC-SHA256 和 token 里带的签名32 个字节逐字节对得上。在线案例与源码https://atomgit.com/deli007/demo_park/tree/main/codearts-jwt-decoder生成物只有一个 index.html纯前端、零依赖、不联网token 和密钥都不出浏览器。一、为什么值得拆开看三段里藏着三个坑JWT 天天在用真正容易出事的地方只有三处payload 是明文、过期时间没人查、算法名写在 token 自己身上。我把这三件事都做成了能点、能改、能当场复现的演示。二、准备环境进入码道 Web把需求写成一段话浏览器打开 https://devcloud.cn-north-4.huaweicloud.com/chat/home 在对话框里把要做的应用说清楚就能拿到一个能跑的单文件应用。本文这个解剖台就是这么来的不用装软件登录华为云账号即可使用。三、JWT 不神秘就是三块拼起来的一段字符串JWT 是三个 base64url 串中间用两个点号连起来。第一段叫 header说明用什么算法签名。第二段叫 payload装业务声明。第三段是签名前两段拼起来的哈希值。我把示例 token 拆开量了一遍三段分别是 36、143、43 个字符。解码之后正好是 27、107、32 字节。这个 4 比 3 的比例不是巧合base64 用 4 个字符表示 3 个字节。四、三段逐字节看长度都是算得出来的页面把每段的细节都摊开了实测数字是这样段解码后内容base64url 长度解码字节数省略的填充符header{alg:HS256,typ:JWT}36 字符27 字节0 个payloadsub / name / iat / nbf / exp / admin143 字符107 字节1 个signatureHMAC-SHA256 的二进制结果43 字符32 字节1 个base64url 和常见的 base64 差两处换成-、/换成_末尾的直接砍掉。砍掉填充符之后长度不再被 4 整除解码前得自己补回来。页面里有一条断言专门盯这件事单字节的YQ省略了 2 个填充符补成YQ才能解出 1 个字节。header 恰好是 36 个字符能被 4 整除所以一个填充符都不用补。五、验签要真算一遍不是看着像我去掉了所有看起来对的判断只用浏览器自带的 Web Crypto API 算真签名。密钥进crypto.subtle.importKey前两段拼起来送进crypto.subtle.sign出来 32 个字节。拿这 32 个字节和 token 第三段解出来的 32 个字节并排对齐逐字节比。这一步没有任何网络请求密钥和 token 全程留在本地。顺手量了一下开销同样的验签跑 2000 次总共 8.5 毫秒平均一次 4.3 微秒。解析 10000 次 header 加 payload 也只要 34.6 毫秒。也就是说校验对接口延迟的影响可以忽略该验的都得验。六、改一个字节签名立刻对不上第三段是哈希payload 动一个字符哈希就全变。我把示例里的 name 改成 Mallory、admin 改成 true签名保持原样再验一次。页面算出的签名是4122d31f88c33cc4…token 里带的是6ae58e1995d0340f…32 个字节里没有一个相同。红色高亮是我特意加的只给一句验签失败没说服力红色字节数会自己说明问题。七、algnone 才是真坑算法写在 token 自己身上签名的算法名写在 header 里这个设计天生别扭。攻击者把 header 改成{alg:none}第三段留空payload 想写什么写什么。验证方如果照着 header 里的alg去选算法看到none就跳过验签伪造的 token 直接过关。正确做法是先定一份算法白名单header 里的alg不在白名单里就拒。页面把两条路都摆出来白名单{HS256}命中不了none拒绝信 header 的老写法none直接放行。同一份输入、两种判断一个拦得住一个拦不住。八、过期检查别把 exp 当装饰payload 里的 iat、nbf、exp 都是 Unix 秒不换算根本看不出问题。页面把它们换算成本地时间再和当前时间比。示例里的过期 tokenexp 是 1791976160换算过来是 2026 年 10 月 14 日 19 时 09 分。同一屏里还有一条正常 tokenexp 写着还有 4 天 23 小时有效用的是绿色。只解码 payload 不判 exp等于发一张永久通行证。九、我踩的坑第一版自检自己都不信页面底部有 12 条断言加载时自动跑结果直接渲染出来这是为了让我自己也没法糊弄。第一版交上来12 条里挂了 1 条。挂的偏偏是最关键的第 6 条正常 token 的 HMAC 验签。原因是它把算出来的签名转成十六进制再和 token 里 base64url 的签名串直接比两个口径不同永远不相等。我用 Python 的 hmac 模块独立算了一遍得到的 base64url 签名auWOGZXQNA_1Nk9I4L6OnNHIokk39bMftkNHtp3oHsM和 token 里带的完全一致说明 token 本身没问题是断言写错了。改成同一个口径再比12 条全过。顺手还修了时间卡片里重复显示的一行状态同一个有效印两遍看着像有两条结论。十、实测数据汇总项目实测值说明token 总长224 字符36 1 143 1 43header27 字节解出alg与typpayload107 字节6 个声明字段signature32 字节HMAC-SHA256 输出HMAC 验签 2000 次8.5 毫秒平均 4.3 微秒一次解码 JSON 解析 10000 次34.6 毫秒平均 3.5 微秒一次篡改 token 相同字节0 / 32整段签名失效页面自检12 / 12 通过加载时自动执行数字都在页面里能复现刷新一次就重跑一遍。十一、本地复现生成物只有一个index.html纯前端、零依赖、不联网。存到本地双击就能打开file://下 Web Crypto 也能正常用。想用静态服务也行python -m http.server 8000然后访问 http://localhost:8000 。替换成自己的 token 时记得把密钥一起改掉HS256 的签名是密钥相关的。十二、用码道做这个小工具的体会码道有三种使用方式WebUI浏览器对话、TUI终端命令行和桌面 IDEIDE 插件。本文用 WebUI 版演示。浏览器打开 https://devcloud.cn-north-4.huaweicloud.com/chat/home 把需求写成一段话发出去就能拿到一个能跑的 index.html。这次最值钱的一条需求是页面里要有自检脚本把每条断言的结果渲染出来。正因为有这条第一版那个十六进制比 base64url 的错才藏不住。另一个体会是要求展示实测值而不是结论验签结果旁边同时给出本页计算值和 token 携带值读者一眼就能自己数相同字节数。不足也说一句这个工具只支持 HS256RS256 这类非对称算法还没做密钥是手填的不能当生产环境的验签服务用。它更适合当作理解 JWT 的解剖台而不是替代后端的鉴权库。
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑