Webhook 详解:签名载荷与可靠重试

Backend2026-10-20TryQuickToolBox

你刚刚集成了第三方支付网关或 CI/CD 服务,现在需要实时通知。Webhook 就是答案——但如果没有适当的安全性和可靠性保障,它们可能会成为令人头疼的来源。攻击者可以伪造请求,网络故障可能导致事件丢失。在本指南中,我们将介绍如何通过签名载荷保护 Webhook 安全,并实现稳健的重试机制。

什么是 Webhook?

Webhook 是由特定事件触发的用户自定义 HTTP 回调。当事件发生时(例如支付成功),源服务会向你提供的 URL 发送一个包含事件数据的 HTTP POST 请求。这种推送模型比轮询更高效,能够实现实时响应。

然而,Webhook 引入了两个挑战:真实性(确保请求来自预期的发送方)和可靠性(确保不遗漏事件)。

使用签名载荷保护 Webhook 安全

签名载荷使用发送方和接收方之间共享的密钥来创建加密签名。发送方将此签名包含在请求头中,接收方验证签名以确认载荷的完整性和来源。

HMAC 签名的工作原理

HMAC(基于哈希的消息认证码)是标准方法。发送方使用共享密钥对请求体计算 HMAC,并将其包含在类似 X-Signature 的请求头中。接收方重新计算 HMAC 并进行比较。

// 示例:在 Node.js 中生成签名
const crypto = require('crypto');

function generateSignature(payload, secret) {
  return crypto
    .createHmac('sha256', secret)
    .update(JSON.stringify(payload))
    .digest('hex');
}

// 在接收端进行验证:
function verifySignature(payload, secret, receivedSignature) {
  const expected = generateSignature(payload, secret);
  // 使用时间安全比较以防止时序攻击
  return crypto.timingSafeEqual(
    Buffer.from(expected, 'hex'),
    Buffer.from(receivedSignature, 'hex')
  );
}

关键要点:

额外的安全措施

实现可靠的重试

网络是不可靠的。Webhook 可能因超时、服务器错误或临时中断而失败。稳健的重试策略能确保最终投递,同时不会压垮你的系统或发送方。

重试最佳实践

  1. 指数退避:增加重试之间的延迟(例如 1s、2s、4s、8s……)。
  2. 最大重试次数:限制尝试次数(例如 5 次)以避免无限循环。
  3. 死信队列:在耗尽重试次数后,存储失败的事件以供人工检查。
  4. 幂等性:确保多次处理同一事件不会产生重复的副作用。

幂等性的实践

在载荷中包含唯一的事件 ID。在接收端,跟踪已处理的 ID(在数据库或缓存中)并忽略重复项。例如:

// 幂等处理的伪代码
if (eventStore.exists(event.id)) {
  return 200; // 已处理过
}
// 处理事件……
eventStore.save(event.id);

优雅地处理失败

快速返回 2xx 状态码以确认收到。如果处理需要时间,将事件加入队列并异步处理。仅对需要重试的临时错误返回 5xx;对于永久性错误(例如无效载荷),返回 4xx 以防止无效重试。

对比:推送 vs. 轮询

方面 Webhook(推送) 轮询
延迟 接近实时 取决于间隔
效率 仅在事件发生时发送 持续请求
复杂度 需要重试/安全逻辑 更简单但浪费资源

测试你的 Webhook 实现

使用 ngrok 等工具暴露本地服务器并模拟 Webhook 投递。通过故意使请求失败来验证签名验证和重试行为。

常见问题

什么是签名的 Webhook 载荷?

签名载荷包含加密签名(通常是 HMAC),允许接收方使用共享密钥验证请求的真实性和完整性。

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

没有通用的次数,但通常采用 3–5 次指数退避重试。之后,记录失败以供人工审查。

可以在没有 HTTPS 的情况下使用 Webhook 吗?

技术上可以,但非常不安全。始终使用 HTTPS 以防止窃听和中间人攻击。

准备好检查 Webhook 载荷了吗?使用我们的 JSON 格式化工具快速美化并验证 JSON 载荷。