3、JWT签名算法:HS256(对称签名)与RS256(非对称签名)的原理与选择场景

JWT 的签名算法决定了令牌的完整性与可信来源。生产环境中最常见的两种算法是 HS256(HMAC with SHA-256,对称签名)与 RS256(RSA with SHA-256,非对称签名)。理解它们的数学基础、密钥管理差异以及适用场景,是构建安全认证契约的第一步。

3.1 核心原理对比

对比维度 HS256(对称) RS256(非对称)
密钥类型 单一共享密钥(Secret) 私钥(Private Key)+ 公钥(Public Key)
签名与验签 签名与验签使用同一个密钥 私钥签名,公钥验签
算法族 HMAC-SHA256 RSA-SHA256
计算开销 低(对称哈希运算) 高(大数模幂运算)
密钥分发 需安全通道共享给所有验证方 公钥可公开分发,私钥仅签名方持有
典型 Token 长度 较短(约 200-300 字符) 较长(约 500-800 字符)

3.2 对称签名:HS256 工作原理

HS256 使用 HMAC(基于哈希的消息认证码)结合 SHA-256 哈希函数。签名方与验证方必须持有完全相同的 共享密钥。签名过程本质上是将 JWT 的 Header 和 Payload 与密钥混合后进行哈希运算,生成一个固定长度的摘要作为签名。

关键约束:共享密钥一旦泄露,攻击者可以伪造任意 JWT。因此 HS256 适用于 单一服务内部 或 完全可信的微服务集群 之间,且密钥必须通过环境变量或密钥管理服务(KMS)注入,禁止硬编码在代码仓库中。

3.3 非对称签名:RS256 工作原理

RS256 基于 RSA 公钥密码体系。签发服务使用 私钥 对 JWT 签名,任何持有对应 公钥 的第三方服务都可以验证签名的真实性,但无法伪造新的令牌。这种“私钥签名、公钥验签”的分离特性,使得 RS256 天然适合 开放生态 与 多客户端验证 场景。

推荐实践:在微服务架构中,建议由统一的认证中心(Auth Service)持有私钥签发 JWT,其他业务服务只持有公钥进行验签。这样即使某个业务服务被攻破,攻击者也无法获取私钥来伪造令牌。

3.4 算法选择决策流程

以下 SVG 图展示了从系统架构角度选择 HS256 或 RS256 的决策路径:

JWT 签名算法选择决策流程 开始选择签名算法 验证方数量 > 1 个独立服务? (是否有多方需要验签) 是 否 HS256 公钥能否安全 分发到各服务? 是 否 HS256 RS256 决策要点说明 • 单一服务内部或完全信任环境 → HS256(性能好,实现简单) • 多服务、跨团队、开放客户端 → RS256(公钥分发,私钥不泄露) • 若无法安全分发公钥(如老旧系统)→ 退化为 HS256 并加固密钥管理 推荐:新系统默认选择 RS256 兼顾安全性与可扩展性

3.5 安全风险与避坑指南

  • 算法混淆攻击:攻击者将 JWT 的 alg 头改为 none 或 HS256(当服务端预期 RS256 时)。务必在验签逻辑中 白名单校验算法类型,拒绝非预期算法。
  • 密钥轮换:HS256 的共享密钥应定期轮换(建议 90 天),且轮换期间需保留旧密钥窗口以验证未过期的 Token。RS256 可通过 kid(Key ID)字段支持多公钥轮换。
  • 密钥长度:HS256 密钥至少 256 位(32 字节);RS256 私钥推荐 2048 位以上,低于 1024 位已被认为不安全。
生产事故案例:某团队在微服务网关中使用 HS256,将共享密钥硬编码在配置文件中并上传至 Git 仓库。内部员工泄露仓库后,攻击者伪造管理员 JWT 获取了全部 API 权限。事后整改为 RS256 + 私钥存储在硬件安全模块(HSM)中。

3.6 代码示例:HS256 与 RS256 签名与验签

以下示例使用 Node.js 的 jsonwebtoken 库展示两种算法的核心差异:

// ========== HS256 对称签名 ==========
const jwt = require('jsonwebtoken');
const SECRET = 'your-256-bit-secret-min-32-chars!!';

// 签名
const hsToken = jwt.sign({ userId: 1001, role: 'admin' }, SECRET, { algorithm: 'HS256', expiresIn: '1h' });

// 验签(同一密钥)
const decoded = jwt.verify(hsToken, SECRET, { algorithms: ['HS256'] });
console.log('HS256 验证通过:', decoded);

// ========== RS256 非对称签名 ==========
const fs = require('fs');
const privateKey = fs.readFileSync('./private.pem', 'utf8');
const publicKey = fs.readFileSync('./public.pem', 'utf8');

// 签名(私钥)
const rsToken = jwt.sign({ userId: 1001, role: 'admin' }, privateKey, { algorithm: 'RS256', expiresIn: '1h' });

// 验签(公钥)
const decodedRS = jwt.verify(rsToken, publicKey, { algorithms: ['RS256'] });
console.log('RS256 验证通过:', decodedRS);

3.7 选择场景总结

场景 推荐算法 理由
单体应用内部 Session 替换 HS256 单一验证方,性能优先,密钥管理简单
微服务架构(认证中心签发) RS256 多服务验签,私钥不泄露,支持公钥公开
第三方开放 API / SSO RS256 客户端可获取公钥自行验证,无需共享密钥
IoT 设备 / 低算力环境 HS256 计算开销低,Token 体积小,适合带宽受限场景
合规要求严格的金融系统 RS256 非对称体系满足审计要求,私钥可硬件隔离
核心原则:能用 RS256 就不用 HS256。非对称签名的安全边界远优于对称签名,尤其在服务数量增长、团队边界扩大的情况下,RS256 的密钥管理成本远低于 HS256 泄露后的灾难恢复成本。