jwt 防重放攻击 为什么攻击者修改随机数后依然无法通过假设攻击者截获了一个合法的请求POST /api/transfer X-Timestamp: 1722240000 X-Nonce: abc123 Authorization: Bearer 合法的JWT现在攻击者想重放这个请求他尝试修改随机数POST /api/transfer X-Timestamp: 1722240000 X-Nonce: xyz789 -- 修改了随机数 Authorization: Bearer 合法的JWT这时候服务端会进行两步校验验证 JWT 签名攻击者手里有原始的 JWT这个 JWT 本身是合法的没过期签名正确。所以这一步能通过。验证请求指纹服务端用用户ID 时间戳 新随机数生成指纹xyz789去 Redis 里查发现没记录过于是放行。看起来攻击成功了并没有因为真正的防重放机制并不是只校验 JWT 和随机数而是将请求的关键部分如时间戳、随机数、甚至请求体与 JWT 绑定在一起进行签名。 真正的防重放请求签名为了防止攻击者篡改请求的任何部分包括随机数我们需要对整个请求进行签名。正确的流程是这样的客户端准备数据生成时间戳X-Timestamp生成随机数X-Nonce准备请求体Body客户端生成签名将X-Timestamp X-Nonce Body拼接起来。使用一个只有客户端和服务端知道的密钥可以是 JWT 的密钥也可以是单独的 AppSecret对这个拼接字符串进行 HMAC-SHA256 等哈希运算生成一个签名Signature。将Signature放入请求头例如X-Signature。客户端发送请求POST /api/transfer X-Timestamp: 1722240000 X-Nonce: abc123 X-Signature: 计算出的签名 Authorization: Bearer 合法的JWT Body: {amount: 100, to: userB}服务端校验第一步验证 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 随机数 请求签名这才是完整的防重放方案。签名确保了请求的完整性任何对请求内容的修改包括随机数都会导致签名校验失败。