4. 密钥生命周期管理基础

JWT 的安全性并不只取决于签名算法本身,更取决于签名密钥在整个生命周期中是否被正确管理。一个使用 HS256 的系统,如果密钥是 "secret"、被硬编码进前端、在多个环境复用、三年不轮换,那么它和没有签名几乎没有区别。密钥生命周期管理要解决的正是这类问题:密钥从哪来、怎么安全地送到使用方、放在哪里、多久换一次、旧密钥怎么处理、最终如何销毁。

NIST SP 800-57 Part 1 把密钥生命周期划分为若干阶段,核心思想是:密钥不是"生成后一直用"的静态资源,而是有明确状态和时限的对象。OWASP 的 JWT 与密钥管理建议则更贴近工程实践,强调密钥强度、环境隔离、轮换机制和泄露响应。本节把两者结合,按六个阶段展开。

一、密钥生成:熵源与算法匹配

密钥生成的第一原则是使用密码学安全的随机数生成器(CSPRNG),而不是 Math.random()、时间戳或自增 ID。对于 HS256,密钥长度应至少等于哈希输出长度,即 256 位(32 字节);对于 RS256/ES256,应使用 2048 位以上 RSA 或 P-256 曲线。生成方式示例:

# 生成 32 字节 HS256 密钥(Base64 编码)
openssl rand -base64 32

# 生成 RSA 2048 私钥
openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -out private.pem

# 生成 EC P-256 私钥
openssl ecparam -name prime256v1 -genkey -noout -out ec-private.pem

生成环境必须是受控的、离线的或受严格访问控制的主机。密钥一旦生成,应立即分配唯一标识(如 kid)和元数据:创建时间、用途、算法、所属环境、预计轮换周期。这些元数据是后续轮换和审计的基础。

二、分发:最小暴露面

密钥分发是最容易出问题的环节。常见错误包括:通过邮件发送、提交到 Git 仓库、写入前端代码、在日志中打印。正确做法是让密钥通过受控通道进入运行时:

  • 使用密钥管理服务(KMS)或密钥库(Vault)动态下发,应用启动时拉取,不落盘。
  • 通过环境变量或挂载的 Secret 卷注入,避免写入镜像层。
  • 分发通道本身要加密并鉴权,接收方需验证来源。

OWASP 特别强调:签名密钥绝不能出现在客户端。JWT 的验证密钥(公钥)可以公开,但签名密钥(私钥/对称密钥)只能存在于服务端可信执行环境中。

三、存储:分级保护

存储策略取决于密钥敏感级别。对称密钥(HS256)和私钥(RS256/ES256)属于最高敏感级别,应存储在硬件安全模块(HSM)、KMS 或专用密钥库中,应用通过 API 调用签名操作,密钥本身不离开安全边界。次一级可用加密的配置中心或 Secret 管理系统,配合静态加密和访问审计。最低要求是:不硬编码、不进版本库、不写日志、不随镜像分发。

常见反模式:把 JWT_SECRET 写进 docker-compose.yml 并提交到仓库;或在 CI 日志中回显环境变量。这类泄露往往在很久后才被发现,且无法判断是否已被利用。

四、轮换:平滑过渡的关键

轮换是指用新密钥替换旧密钥,同时保证已签发令牌仍可验证。JWT 的无状态特性使轮换变得微妙:如果直接替换密钥,所有旧令牌立即失效,用户被迫重新登录。工程上通常采用"双密钥过渡":

  1. 生成新密钥,分配新的 kid,加入 JWKS(JSON Web Key Set)。
  2. 签发端开始用新密钥签名,令牌头部携带新 kid。
  3. 验证端根据 kid 从 JWKS 选择对应密钥,旧令牌仍用旧密钥验证。
  4. 等待旧令牌最大有效期(如 24 小时)过后,从 JWKS 移除旧密钥。

轮换周期没有统一标准。NIST 建议根据密钥用途和风险设定密码期(cryptoperiod),高价值密钥可短至数月,低风险可一年。OWASP 更关注"是否具备轮换能力"——即使不频繁轮换,也必须能在泄露时快速完成。下面这张图展示了双密钥过渡期间,签发与验证两侧如何按 kid 协同:

JWT 密钥轮换:双密钥过渡与 kid 路由 T0 旧密钥 T1 新密钥加入 T2 旧令牌过期 T3 移除旧密钥 签发端(Auth Service) T0–T1:用旧密钥签名,kid=old T1 起:用新密钥签名,kid=new 令牌头部携带 kid,供验证端路由 验证端(Resource Server) 读取 JWKS,按 kid 选择公钥/密钥 kid=old → 旧密钥验证(仍有效) kid=new → 新密钥验证 JWKS 端点 同时发布 old + new 公钥 过渡窗口 = 旧令牌最大有效期;窗口内两把密钥并存,窗口结束才可安全移除旧密钥

五、归档与销毁

归档针对的是已停用但可能仍需验证历史令牌的密钥。例如审计要求保留过去 90 天的令牌验证能力,则旧密钥不能立即删除,而应移入归档区:只读、禁止签发、访问受审计、设置明确的保留期限。归档密钥与活跃密钥必须物理或逻辑隔离,避免误用。

销毁是生命周期的终点。NIST 要求密钥销毁应使其不可恢复,包括覆盖存储介质、撤销 KMS 中的密钥材料、清除缓存和备份中的副本。销毁前需确认:没有未过期令牌仍依赖该密钥、没有正在进行的验证请求、归档保留期已到。销毁动作本身应记录审计日志,包含密钥 ID、销毁时间、操作人和依据。

六、NIST 与 OWASP 要点对照

关注点NIST SP 800-57 侧重OWASP 侧重
生成规定密钥强度、密码期、状态模型强调 CSPRNG、禁止弱密钥与默认密钥
分发密钥传输保护、实体鉴权禁止密钥进入客户端、日志、仓库
存储按敏感级别分级,HSM/KMS环境隔离、Secret 管理、最小权限
轮换设定密码期,到期必须更换必须具备快速轮换与泄露响应能力
归档/销毁明确保留期与不可恢复销毁审计可追溯,避免僵尸密钥

把六个阶段串起来看,密钥治理的本质是"可控、可换、可查"。可控指生成和存储受严格保护;可换指轮换不影响业务连续性;可查指每一步都有元数据和审计记录。对 JWT 系统而言,最实用的落地起点是:立即检查是否存在硬编码密钥,建立 JWKS 端点,实现基于 kid 的双密钥轮换,并为每把密钥记录创建时间与轮换期限。做到这四点,就已经覆盖了生命周期管理中最关键的风险面。