极简工具箱

免费在线工具 · 本地计算 · 无需上传

JWT 的五个安全陷阱:alg=none、密钥弱、无法注销

JWT 用起来很顺手,但「自带状态」这个优点同时也是它的软肋:签出去就撤不回,Payload 还是明文。上线前把这五个坑过一遍。

广告位(百度联盟)

一、alg: none 与算法混淆

JWT 头部声明了签名算法。如果服务端信任这个字段,就可能被攻击:

防护只有一条:服务端硬编码允许的算法白名单,忽略客户端声明的 alg。

jwt.verify(token, key, { algorithms: ['RS256'] });  // 必须显式指定

二、密钥太弱

HS256 的密钥就是一个字符串。用 secret、123456、项目名当密钥,等于没有签名——攻击者离线暴力破解后可以伪造任意 token。

建议:至少 32 字节的密码学随机值,用 crypto.randomBytes(32).toString('base64url') 生成,通过环境变量或 KMS 注入,定期轮换(轮换时用 kid 头部支持多密钥并存)。

三、Payload 是明文

Base64URL 只是编码。任何人拿到 token 都能读出里面的字段,包括用户 ID、角色、手机号。所以:

一个常见错误是把权限列表放 Payload 后直接信任它。如果角色变了,旧 token 在过期前仍然带着旧权限;反过来,如果权限判断依赖 payload,就要接受「权限变更延迟到 token 过期」这个事实。

四、无法主动注销

JWT 是自包含的,服务端不存状态,所以签发后无法撤回。用户点「退出登录」、改了密码、账号被封,旧 token 在 exp 之前依然有效。

几种折中方案:

方案代价
短有效期(15 分钟)+ Refresh Token推荐,泄露窗口小
服务端维护黑名单(jti 或用户级 tokenVersion)引入状态,但可控
改密码时更新用户 tokenVersion,校验时比对只解决「改密码后失效」
纯前端删除 token只是「看不见」,不防重放

生产环境通常是「短 access token + 长 refresh token + refresh 时校验用户状态」的组合。

五、存在哪里

位置XSS 风险CSRF 风险说明
localStorage高(脚本可读)无方便,但一旦有 XSS 就全丢
内存变量低无刷新页面即失效,需配合 refresh
HttpOnly Cookie低高(需 SameSite + CSRF token)推荐,但要防 CSRF

没有完美选项。比较稳的组合是:refresh token 放 HttpOnly + Secure + SameSite=Lax 的 Cookie,access token 只放内存,请求时放 Authorization 头。

六、还有两个细节

调试时用 JWT 解析工具 看三段内容和过期时间(本地解码,不上传);需要复核签名计算时,用 HMAC 生成 对 header.payload 做一次,再与签名段比对。

小结

算法白名单、密钥够长、Payload 不放敏感信息、短有效期 + Refresh、HttpOnly 存 refresh。JWT 的便利来自无状态,风险也全部来自无状态。

本文相关工具(本地计算,无需上传)

更多使用教程