jwt 防重放攻击
2026/7/30 7:17:36 网站建设 项目流程

🤔 为什么攻击者修改随机数后依然无法通过?

假设攻击者截获了一个合法的请求:

POST /api/transfer X-Timestamp: 1722240000 X-Nonce: abc123 Authorization: Bearer <合法的JWT>

现在,攻击者想重放这个请求,他尝试修改随机数:

POST /api/transfer X-Timestamp: 1722240000 X-Nonce: xyz789 <-- 修改了随机数 Authorization: Bearer <合法的JWT>

这时候,服务端会进行两步校验:

  1. 验证 JWT 签名:攻击者手里有原始的 JWT,这个 JWT 本身是合法的(没过期,签名正确)。所以这一步能通过。
  2. 验证请求指纹:服务端用用户ID + 时间戳 + 新随机数生成指纹xyz789,去 Redis 里查,发现没记录过,于是放行。

看起来攻击成功了?并没有!

因为真正的防重放机制,并不是只校验 JWT 和随机数,而是将请求的关键部分(如时间戳、随机数、甚至请求体)与 JWT 绑定在一起进行签名

🔒 真正的防重放:请求签名

为了防止攻击者篡改请求的任何部分(包括随机数),我们需要对整个请求进行签名。

正确的流程是这样的:
  1. 客户端准备数据

    • 生成时间戳X-Timestamp
    • 生成随机数X-Nonce
    • 准备请求体Body
  2. 客户端生成签名

    • X-Timestamp + X-Nonce + Body拼接起来。
    • 使用一个只有客户端和服务端知道的密钥(可以是 JWT 的密钥,也可以是单独的 AppSecret)对这个拼接字符串进行 HMAC-SHA256 等哈希运算,生成一个签名Signature
    • Signature放入请求头(例如X-Signature)。
  3. 客户端发送请求

    POST /api/transfer X-Timestamp: 1722240000 X-Nonce: abc123 X-Signature: <计算出的签名> Authorization: Bearer <合法的JWT> Body: {"amount": 100, "to": "userB"}
  4. 服务端校验

    • 第一步:验证 JWT,确认用户身份。
    • 第二步:验证签名。服务端用同样的密钥,对收到的X-Timestamp + X-Nonce + Body重新计算一遍签名。
    • 第三步:比对签名。如果服务端计算出的签名和请求头里的X-Signature一致,说明请求内容(包括时间戳、随机数、请求体)在传输过程中没有被篡改
    • 第四步:防重放检查。将X-Timestamp + X-Nonce存入 Redis,检查是否重复。

💡 思考

如果攻击者现在尝试修改随机数:

POST /api/transfer X-Timestamp: 1722240000 X-Nonce: xyz789 <-- 修改了随机数 X-Signature: <原始的签名> <-- 签名没变 Authorization: Bearer <合法的JWT> Body: {"amount": 100, "to": "userB"}

服务端在校验时,会用新的X-Nonce重新计算签名,结果会发现计算出的签名和请求头里的X-Signature不一致,于是直接拒绝请求。

总结一下:

  • 只靠 JWT + 随机数:确实防不住篡改随机数的重放。
  • JWT + 随机数 + 请求签名:这才是完整的防重放方案。签名确保了请求的完整性,任何对请求内容的修改(包括随机数)都会导致签名校验失败。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询