Webhooks 詳解:簽名載荷與重試機制

Backend2026-11-09TryQuickToolBox

你剛剛整合了支付閘道或 CI/CD 服務,現在需要接收即時事件。Webhooks 是標準解決方案,但它們帶來兩個關鍵挑戰:驗證請求確實來自發送方,以及確保伺服器暫時無法使用時事件不會遺失。本文說明如何實作簽名載荷和重試機制,以建構穩健的 webhook 接收器。

什麼是 Webhooks?

Webhook 是一種 HTTP 回呼:每當事件發生時,服務會向您提供的 URL 發送 HTTP POST 請求,而不是讓您的應用程式輪詢 API 以獲取更新。這種推送模型可降低延遲和伺服器負載。然而,由於請求來自外部服務,您必須將其視為不受信任的輸入。

為什麼要簽名載荷?

未經驗證,任何發現您的 webhook 端點的人都可以發送偽造事件,可能觸發未授權的操作。使用共享密鑰對載荷進行簽名,可確認發送方的身分並確保載荷未被篡改。最常見的方法是使用 SHA-256 的 HMAC(基於雜湊的訊息驗證碼)。

HMAC 簽名如何運作

發送方使用密鑰計算原始請求主體的 HMAC 摘要,並將其包含在標頭中(例如 X-Signature)。您的伺服器使用相同的密鑰重新計算 HMAC,並與收到的簽名進行比較。如果匹配,則請求是真實的。

// Node.js 範例:驗證 webhook 簽名
const crypto = require('crypto');

function verifySignature(payload, signature, secret) {
  const hmac = crypto.createHmac('sha256', secret);
  hmac.update(payload);
  const digest = hmac.digest('hex');
  // 使用 timingSafeEqual 防止時序攻擊
  return crypto.timingSafeEqual(
    Buffer.from(digest, 'hex'),
    Buffer.from(signature, 'hex')
  );
}

重要: 始終使用常數時間比較函數(如 timingSafeEqual),以避免透過時序差異洩露資訊。此外,請對原始主體計算 HMAC,而不是對解析後的 JSON,因為重新序列化可能會改變位元組序列。

實作重試

即使有簽名,網路問題或伺服器錯誤也可能導致 webhook 交付失敗。可靠的發送方會重試失敗的交付,通常採用指數退避。作為接收方,您應將端點設計為冪等,並返回適當的狀態碼。

從發送方角度的重試策略

設計冪等接收器

因為重試可能導致重複交付,您的接收器必須能夠多次處理同一事件而不產生不良影響。在載荷(或標頭)中包含唯一的事件 ID,並儲存已處理的 ID。如果事件 ID 已被處理,立即返回 200 OK,無需重新處理。

// 冪等 webhook 處理的虛擬碼
function handleWebhook(event) {
  if (processedEvents.has(event.id)) {
    return { status: 200, body: 'Already processed' };
  }
  // 處理事件...
  processedEvents.add(event.id);
  return { status: 200, body: 'Success' };
}

Webhook 安全性與可靠性的最佳實踐

比較:輪詢 vs. Webhooks

方面輪詢Webhooks
延遲取決於間隔近乎即時
伺服器負載高(頻繁請求)低(僅在事件發生時)
複雜度實作簡單需要簽名驗證和重試處理
可靠性您控制重試取決於發送方的重試策略

常見問題

如何驗證 webhook 簽名?

使用共享密鑰計算原始請求主體的 HMAC,然後使用常數時間比較函數將其與標頭中的簽名進行比較。

我的 webhook 端點應該返回什麼?

一旦收到事件,立即返回 2xx 狀態碼(例如 200 OK)。如果您返回 4xx 或 5xx,發送方可能會重試或將交付標記為失敗。

我可以不使用簽名來使用 webhooks 嗎?

不建議這樣做。沒有簽名,您無法驗證發送方,使您的端點容易受到偽造請求的攻擊。在生產環境中始終使用簽名。

在除錯 webhook 載荷時,JSON 格式化至關重要。使用我們的 JSON 格式化工具 快速美化並驗證傳入的 JSON 資料。