JWT 签名算法选型:HS256、RS256、ES256、PS256 到底用哪个

· 更新于 2026-05-02 · 约 5 分钟 🪪 JWT 签发

JWT 的“签名算法”看似只是 header 里一个 alg 字段,但它背后影响的是密钥体系、验签权限边界和部署复杂度。HS256、RS256、ES256、PS256 这几类就足够覆盖大多数项目;真正重要的不是背参数,而是先判断你是不是需要把“签发权”和“验证权”分开。

一张算法对照表

算法类型密钥体系签名长度速度典型场景
HS256HMAC-SHA256对称(共享 secret)32 字节极快单体应用、Webhook
HS384HMAC-SHA384对称48 字节极快同上,需更高安全
HS512HMAC-SHA512对称64 字节同上,最高强度
RS256RSA-PKCS1-SHA256非对称 RSA256 字节微服务、OAuth
RS384/RS512同上非对称 RSA384/512 字节高合规场景
ES256ECDSA-P256-SHA256非对称 EC64 字节现代 OAuth/OIDC
ES384ECDSA-P384-SHA384非对称 EC96 字节中快政府/军工
PS256RSA-PSS-SHA256非对称 RSA256 字节FIPS 合规

本站 jwt-sign 工具当前支持 HS256/384/512、RS256/384/512、PS256/384/512、ES256/384/512,不包含 EdDSA。

光看这张表难判断,下面按场景拆解。

单方签发自验:HS256

特征:发 token 和验 token 都是同一个服务(或同一组对等服务),不需要把验证权下放给第三方。

典型场景

  • 单体 Web 应用:应用自己发 token、自己验 token
  • Webhook 验签:GitHub、Stripe、Shopify 给你一个 secret,发 webhook 时签名,你用同一 secret 验
  • 内部 API:网关签发,业务服务用 secret 验
  • IoT 设备 token:设备和服务器预共享 key

为什么是 HS256

  • 密钥就一个 secret,无需 PKI 基建
  • 签发和验证都是 HMAC,性能通常不是瓶颈
  • 部署只是把 secret 塞进环境变量

注意事项

  • secret 至少 32 字节,从 crypto.randomBytes(32)
  • 任何持有 secret 的服务都能伪造任意 token——所以 secret 不要广撒
  • 密钥泄露 = 全员被盗,必须立即轮换

微服务、跨团队:RS256 / ES256

特征:签发服务和验证服务不是同一个,需要”签发权与验证权分离”。

典型场景

  • 鉴权中心签发 token,订单服务、支付服务、物流服务各自验证
  • B2B 平台:你签发 token,第三方业务方持你的公钥验证
  • 单点登录 SSO:IdP(Identity Provider)签,多个 SP(Service Provider)验

为什么常不选 HS256:HS256 的 secret 一旦发给验证服务,那个服务也具备伪造能力,签发权和验证权就分不开了。

RS256 vs ES256

维度RS256ES256
公钥大小≈270 字节≈64 字节
私钥大小≈1700 字节≈120 字节
签名长度256 字节64 字节
token 总长短 30-40%
签名性能中等
验证性能中等
算法成熟度极高
移动端友好一般

实务

  • 新项目可以优先在 RS256 / ES256 之间选:ES256 更省字节,RS256 生态更稳
  • 已有 RS256 不必急着切:只要密钥管理和验签配置正确,继续用完全合理
  • OAuth/OIDC 标准化场景:双方都接受时优先 ES256

高合规场景:PS256

特征:受 FIPS 140-3、PCI DSS 4.0、政府/军工等监管约束。

为什么是 PS256:PS256 用 RSA-PSS,是 RSA 签名的现代化版本,引入随机 salt,有可证明安全的密码学证明。RS256 用的 PKCS#1 v1.5 在工程上没有已知漏洞,但密码学界认为 PSS”更现代”。

实际取舍

  • 没有合规要求时,PS256 性能略低于 RS256,没有显著优势
  • 库支持比 RS256 略弱——部分老库(早期 Java、PHP 库)不支持
  • 切换成本和 RS256 差不多,但收益主要是”满足审计”而非真实安全提升

结论:除非合规审计要求,否则跳过 PS256,直接用 RS256 或 ES256。

密钥长度指引

算法推荐密钥长度最低
HS25632 字节(256 位)32 字节
HS38448 字节48 字节
HS51264 字节64 字节
RS256/PS256RSA-2048RSA-2048
RS384/PS384RSA-3072RSA-2048
ES256EC P-256EC P-256
ES384EC P-384EC P-384

HS 密钥openssl rand -base64 32crypto.randomBytes(32).toString('base64') 生成,不要自己拍字符串。

RSA 密钥

openssl genrsa -out priv.pem 2048
openssl rsa -in priv.pem -pubout -out pub.pem

EC 密钥(P-256):

openssl ecparam -genkey -name prime256v1 -noout -out priv.pem
openssl ec -in priv.pem -pubout -out pub.pem

时效字段:iat / exp / nbf 怎么设

签发时至少带这三个字段:

{
  "iat": 1714200000,
  "exp": 1714201800,
  "nbf": 1714200000,
  "sub": "user_12345",
  "role": "user"
}
字段含义推荐值
iat签发时间(Unix 秒)当前时间
exp过期时间iat + 15 分钟(access)/ + 7 天(refresh)
nbf生效时间等于 iat 或省略
jtitoken 唯一 ID用于黑名单/吊销
iss签发方你的服务标识
aud预期接收方验证端要校验
sub主体(通常 user_id)必填

短 access + 长 refresh:access 15 分钟到期,refresh 7-30 天,业务服务只验 access,refresh 只在 /refresh 端点用。这样 access 被盗的时间窗口短,refresh 可以在服务端立即吊销。

验签时的硬规则:算法白名单

无论用什么算法,验签代码必须写死白名单

// ❌ 错:信 header 里的 alg
jwt.verify(token, key);

// ✅ 对:白名单
jwt.verify(token, key, { algorithms: ['RS256'] });

不写死会被 alg confusion 攻击:攻击者把 RS256 token 的 header 改成 alg: HS256,用你公开的 RSA 公钥当 HMAC secret 重签——服务端按 HS256 验证,公钥正好等于 secret,验证通过。

更狠的 alg: none 攻击:攻击者把 alg 改成 "none"、签名段留空、payload 随便改——老库直接放行。

白名单是 JWT 验签的第一防线,比选哪个算法更重要

选型决策树

1. 签发方和验证方是同一个吗?
   ├─ 是  → HS256(密钥 ≥ 32 字节)
   └─ 否  → 继续
2. 有 FIPS / 政府合规要求吗?
   ├─ 是  → PS256 / ES384
   └─ 否  → 继续
3. 移动端 / 流量敏感?
   ├─ 是  → ES256
   └─ 否  → RS256(成熟)/ ES256(更现代)

绝大多数场景,HS256 + RS256 + ES256 三个就够覆盖。再往外的算法属于”特定合规需求”或”前沿尝鲜”,按需引入即可。

❓ 常见问题

HS256 和 RS256,怎么选?

同一信任域内自签自验,常先看 HS256;需要把签发权和验证权分开,常先看 RS256 或 ES256。HS256 只维护一把共享 secret,部署简单,但任何持有 secret 的服务都具备伪造能力。RS256 / ES256 用私钥签、公钥验,更适合多服务或第三方验证。

ES256 比 RS256 好在哪里?为什么 OAuth 新规范偏爱它?

ES256 是 ECDSA + P-256 + SHA-256,主要优势是密钥小、签名小、性能不差对比 RS256:(1) 公钥——RSA-2048 公钥约 270 字节,EC P-256 公钥约 64 字节;(2) 签名长度——RS256 签名 256 字节,ES256 签名 64 字节;(3) 生成速度——签名 ECDSA 比 RSA-2048 快约 5-10 倍;(4) 验证速度——RSA 公钥验证略快,ECDSA 验证略慢,但都在毫秒级;(5) token 整体小 30-40%——网络传输、Cookie 存储更省。OAuth/OIDC 偏爱原因:(1) JWKS 文件更小,下载快;(2) 移动端 / IoT 场景节省字节;(3) NIST 推荐曲线(P-256/P-384)密码学评级高;(4) 与现代 TLS 1.3、Web Push、WebAuthn 的密钥体系统一。唯一缺点:ECDSA 签名强随机性要求——签名时若 nonce 重复或可预测,私钥可被反推(PS3 案例)。所有合规库自动用 RFC 6979 的确定性 nonce,不要自己实现签名。

HS256 的 secret 多长才够?设个短的会怎样?

HS256 至少用 32 字节高熵随机密钥。RFC 7518 对 HS256 的要求就是密钥长度至少不小于哈希输出长度。短 secret、字典词或可预测随机源都会让离线穷举风险显著上升。

PS256 比 RS256 强在哪里?要不要切?

PS256 = RSA-PSS + SHA-256,是 RSA 签名的现代化版本PKCS#1 v1.5(RS256 用的)vs PSS:(1) PKCS#1 v1.5 是确定性签名——同一消息每次签出一样的结果;(2) PSS 引入随机 salt——每次签出的结果不同,但都能验证通过;(3) PSS 有可证明安全(provable security)的形式化证明,PKCS#1 v1.5 没有;(4) NIST 推荐新系统用 PSS。实际差异:(1) 现实中 RS256 没有已知可利用攻击——纯密码学层面 PSS 更"现代",但工程上两者都安全;(2) PSS 比 PKCS#1 v1.5 慢一点(多算 salt 和 mask);(3) 库支持——RS256 几乎所有 JWT 库都支持,PS256 部分老库不支持;(4) 标准要求——FIPS 140-3、PCI DSS 4.0 等推荐 PSS。实务:(1) 全新项目可以选 PS256;(2) 已有 RS256 不需要紧急切;(3) 如果监管要求 FIPS / 合规审计可能要求 PSS——按合规需求决定;(4) 多数公司直接跳过 PS256,从 RS256 切到 ES256,因为 EC 优势更明显。

iat、exp、nbf 该设多长?JWT 不能太短也不能太长?

短 access + 长 refresh 是标准模式iat(Issued At) 签发时间——直接当前秒数(Math.floor(Date.now()/1000)),别用毫秒或字符串。exp(Expiration) 过期时间——access token 15 分钟,refresh token 7-30 天nbf(Not Before) 生效时间——通常等于 iat 或省略;只在"延迟生效 token"(如预约访问)才设置。为什么 access 要短:(1) JWT 无状态——服务端无法主动作废,过期是唯一保护;(2) 被窃后窗口越短损失越小;(3) 短期 token 让"用户在 A 设备改了权限,B 设备 15 分钟后就生效"成为可能。为什么 refresh 要长:(1) 用户体验——长 refresh 才能"7 天免登录";(2) refresh 必须存服务端可吊销列表,被盗能立即作废;(3) refresh 端点要严格防 CSRF + 防爆破。典型坑:(1) exp 设成 1 年——等于把无状态 JWT 当 session cookie 用,被盗后只能干等;(2) iat 没设——服务端无法判断"这个 token 是不是改密码前签发的";(3) 时钟漂移——服务器之间相差 30 秒就有 token "未生效"或"已过期",验证端要留 leeway。

JWT 算法字段 alg 在哪个位置?为什么强调“不能信 header 的 alg”?

alg 在 JWT 的 header 里。但验签端不能因为 token 自己写了 alg 就跟着切算法。更稳妥的做法是服务端自己配置允许的算法白名单,例如只接受 RS256 或只接受 HS256,否则就会落入经典的 alg 混淆风险。

🪪 打开 JWT 签发 HS/RS/ES/PS 全套·PEM/JWK 私钥·iat/exp 快捷·WebCrypto 本地签名密钥不上传

📖 同一工具的其他教程