JWT 签名不是加密——Token 里装的全是明文

· 约 3 分钟 🎟️ JWT 解析

“JWT 安全吗?“——安全性取决于你怎么用。九成问题出在一个误解上:JWT 是签名,不是加密。token 里的内容任何人都能看。

JWT 长什么样

eyJhbGciOiJIUzI1NiJ9.eyJ1c2VySWQiOjEyMyx
← header                ← payload
LCJleHAiOjE3NjM0NTYwMH0.sflKxwRJSMeKKF2Q
T4fwpMeJf36POk6yJV_adQssw5c
                        ↑ signature

三段用 . 分隔:

  1. Header{"alg":"HS256","typ":"JWT"} Base64URL
  2. Payload{"userId":123,"exp":1763456000} Base64URL
  3. SignatureHMAC-SHA256(header.payload, secret)

前两段只是编码,不是加密。粘进解析工具秒看明文。

误解一:payload 加密过了

没有。Base64URL 是可逆编码,浏览器控制台一行就解:

atob('eyJ1c2VySWQiOjEyM30')
// '{"userId":123}'

所以密码、完整身份证、银行卡号绝对不能放 payload。放了等于在数据库外面贴张便利贴。

误解二:签名能阻止篡改

能——但前提是服务端真的校验了签名。历史上有两个经典漏洞:

alg: none 绕过

JWT 规范允许 alg: "none" 表示”不签名”。部分库在校验时傻乎乎地按 header 里的 alg 来——攻击者把 header 改成 {"alg":"none"}、签名段留空,payload 随便改(比如把 role:"user" 改成 role:"admin"),服务端居然通过。

防御:服务端白名单签名算法,jwt.verify(token, secret, { algorithms: ['HS256'] })——把 algorithms 写死,别信 header。

HS256 / RS256 混淆攻击

  • RS256 用公钥验证、私钥签发
  • HS256 用同一个 secret签发和验证

若服务端配成”验证时 alg 取 header”,攻击者:

  1. 拿到你公开的 RS256 公钥
  2. 构造一个 token,header 写 {"alg":"HS256"}
  3. 用那把公钥当作 HS256 的 secret 签名
  4. 服务端按 HS256 校验,secret 正好是公钥——验证通过

防御:同上,算法写死别跟着 header 走。

误解三:JWT 可以”登出”

标准 JWT 是无状态的,只要签名有效、没到期,服务端就认。“登出”在纯 JWT 下实际上做不到。

常见折中:

  • 短过期时间(15 分钟)+ refresh token 机制
  • 服务端黑名单:已退出的 jti 存 Redis,到期前拦截
  • token 版本号:数据库里存 user.tokenVersion,payload 带上;改密码时 tokenVersion+1 让所有老 token 失效

把 JWT 当 session 用、设 7 天过期、不带黑名单——被盗号后只能干等七天。

误解四:JWT 是 OAuth

不是。OAuth 是授权协议,JWT 是token 格式。OAuth 可以用 JWT 做 access token,也可以用别的(比如随机字符串 + 服务端 session)。反过来 JWT 也能单独用——很多简单后端自签自验不走 OAuth。

标准 claim 速查

Payload 里这些 key 是 RFC 7519 定义的”注册 claim”,别随便占用:

claim全称含义
issIssuer签发方
subSubjecttoken 主体(通常 userId)
audAudience预期接收方
expExpiration Time过期时间(Unix 秒)
nbfNot Before生效时间
iatIssued At签发时间
jtiJWT ID唯一 ID(用于黑名单)

其他业务字段随便加,但建议用短名字(payload 越小 token 越短)。

一次 JWT 体检清单

  1. 算法白名单是否写死?algorithms: ['HS256'] 不是 null
  2. exp 是否设置?短有效期是关键防线
  3. secret 是否足够强?HS256 的 secret 至少 32 字节随机
  4. 敏感信息是否放了 payload?身份证号/手机号/密码都算
  5. 多服务架构是否升级到 RS256?让其他服务只持公钥
  6. 是否能在服务端吊销?至少有黑名单或 tokenVersion 机制

实时看 token 内容

粘贴 token,三段着色展开 header / payload / signature,exp / iat 自动换成本地时间,能立刻看到当前 token 还剩多久、到底装了什么——本地解析,token 不上传。

❓ 常见问题

JWT 的 payload 能放密码吗?

绝对不行。JWT 的 payload 是 Base64URL 编码,不是加密——任何人拿到 token,解码第二段就能看到明文。密码、身份证号、银行卡号、内部 ID 都不能放。只放"用于识别身份的最小信息":user_id、角色、过期时间。

JWT 可以在客户端存吗?

可以,但要选对存储。localStorage 有 XSS 风险(任意脚本可读);httpOnly Cookie 能防 XSS 但有 CSRF 风险(需配合 SameSite)。高敏感场景(银行、医疗)建议短 access token + refresh token 双 token 架构,access 放内存,refresh 放 httpOnly Cookie。

JWT 过期后怎么刷新?

发两个 token:access token 短(15 分钟),refresh token 长(7 天)。access 过期后用 refresh 向服务端换新的 access。refresh 必须存在服务端数据库,随时能吊销——JWT 本身无法"退出登录",只能靠服务端黑名单或短有效期+吊销列表。

签名算法写的 HS256 和 RS256 有什么区别?

HS256 是对称加密,签发和验证用同一个 secret——适合单体应用。RS256 是非对称加密,私钥签发、公钥验证——适合多服务架构(鉴权服务发 token,其他服务只用公钥验证,私钥不泄漏)。混用两者会引发经典攻击,见下文。

🎟️ 打开 JWT 解析 Token 三段着色 · exp/iat 时间展示 · 本地解析

📖 同一工具的其他教程