Webhook 详解:签名载荷与重试策略

Backend2026-10-21TryQuickToolBox

你已经构建了一个 API,向用户的端点发送 Webhook。但随后你面临经典问题:如何确保载荷未被篡改?当端点宕机时会发生什么?如何防止重复处理?在本文中,我们将通过签名载荷和稳健的重试策略正面解决这些挑战。

为什么 Webhook 需要签名

Webhook 是通知其他系统事件的 HTTP 回调。由于它们通过公共互联网发送,因此容易受到拦截和欺骗。没有签名,恶意行为者可以向你的用户发送虚假 Webhook,触发意外操作。使用共享密钥对载荷进行签名可确保真实性和完整性。

签名载荷的工作原理

最常见的方法是使用 HMAC(基于哈希的消息认证码)与 SHA-256。发送方和接收方共享一个密钥。发送方对载荷(有时包括时间戳)计算签名,并将其包含在头部中。接收方重新计算签名并与接收到的签名进行比较。

分步指南:签名载荷

  1. 创建载荷:将事件数据序列化为 JSON。确保格式一致(例如,没有多余空格),因为签名是基于确切的字节计算的。
  2. 添加时间戳:在载荷或头部中包含 Unix 时间戳以防止重放攻击。接收方可以拒绝超过几分钟的请求。
  3. 计算 HMAC:使用你的密钥生成载荷字符串(或时间戳 + 载荷)的 HMAC-SHA256。
  4. 附加签名:在像 X-Signature 这样的头部中发送它。

以下是 Python 示例:

import hmac
import hashlib
import json
import time

secret = b'your_shared_secret'
payload = {"event": "user.created", "data": {"id": 123}}
body = json.dumps(payload, separators=(',', ':'))
timestamp = str(int(time.time()))
signed_content = timestamp + '.' + body
signature = hmac.new(secret, signed_content.encode(), hashlib.sha256).hexdigest()

# 发送头部: X-Signature: signature, X-Timestamp: timestamp

验证签名

在接收端,你必须:

Node.js 验证示例:

const crypto = require('crypto');

function verifySignature(req, secret) {
  const signature = req.headers['x-signature'];
  const timestamp = req.headers['x-timestamp'];
  if (!signature || !timestamp) return false;
  if (Date.now() / 1000 - timestamp > 300) return false; // 5 分钟
  const body = JSON.stringify(req.body);
  const signed = timestamp + '.' + body;
  const expected = crypto.createHmac('sha256', secret).update(signed).digest('hex');
  return crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected));
}

设计重试策略

即使有签名,Webhook 也可能因网络问题、服务器错误或超时而失败。稳健的重试策略可确保交付,同时不会给接收方造成过大负担。

关键考虑因素

实现重试

大多数 Webhook 发送方使用队列系统(例如,Redis、RabbitMQ 或数据库)来安排重试。以下是一个简化流程:

  1. 事件触发时,将带有载荷和目标 URL 的 Webhook 作业入队。
  2. 工作进程获取作业并尝试交付。
  3. 如果响应状态为 2xx,则标记为成功。
  4. 如果不是,则使用指数退避安排重试,直到最大尝试次数。
  5. 达到最大尝试次数后,标记为失败并通知。

重试计划示例:

尝试延迟
1立即
21 分钟
35 分钟
430 分钟
52 小时

Webhook 安全最佳实践

常见问题

什么是签名的 Webhook 载荷?

签名载荷包含一个加密签名(通常是 HMAC),允许接收方验证发送方的真实性以及载荷未被篡改。

应该重试失败的 Webhook 多少次?

没有一刀切的答案,但常见的方法是使用指数退避重试 3–5 次。考虑事件的关键性和接收方的容量。

我可以使用 JWT 作为 Webhook 签名吗?

是的,可以使用 JWT,但它更复杂。HMAC 更简单,足以满足大多数用例。如果你需要在签名本身中包含声明或过期时间,JWT 很有用。

准备好测试你的 Webhook 载荷了吗?使用我们的 JSON Formatter 在发送前验证和美化你的 JSON 载荷。