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

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()

# Send headers: 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 min
  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 格式化工具 在发送前验证和美化您的 JSON 载荷。