Webhooks 詳解:簽章負載與重試機制

Backend2026-10-14TryQuickToolBox

為什麼 Webhooks 很棘手

Webhooks 的概念很簡單:與其輪詢 API 來檢查變更,不如讓提供者透過 HTTP POST 將事件推送到你的端點。但在實務上,Webhooks 帶來了一系列挑戰,讓許多開發者頭痛:驗證請求確實來自提供者、處理重複傳送,以及應對網路故障。如果忽略這些問題,你可能會面臨安全漏洞、資料損毀和使用者不滿。

本文將探討生產級 Webhooks 的兩個最關鍵面向:簽章負載與重試。我們將涵蓋如何驗證簽章、如何使用指數退避實作重試,以及如何讓處理過程具備幂等性。

簽章負載:證明真實性

任何人都可以向你的端點發送 HTTP POST。為了確保請求來自你的提供者,提供者會使用密鑰對負載進行簽章,並在標頭中傳送簽章。你的任務是重新計算簽章並進行比對。

大多數提供者使用 HMAC-SHA256。簽章通常是對原始請求主體(有時包含時間戳)計算,並以十六進位編碼。例如,Stripe 使用類似 Stripe-Signature 的標頭,其中包含時間戳和簽章。

HMAC 如何運作

HMAC(基於雜湊的訊息驗證碼)將密鑰與雜湊函數結合。給定相同的密鑰和訊息,輸出是確定性的。沒有密鑰,攻擊者無法偽造有效的簽章。

以下是 Node.js 中的一個最小範例:

const crypto = require('crypto');

function verifySignature(payload, signature, secret) {
  const expected = crypto
    .createHmac('sha256', secret)
    .update(payload)
    .digest('hex');
  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expected)
  );
}

在 Python 中同樣簡單:

import hmac
import hashlib

def verify_signature(payload: bytes, signature: str, secret: str) -> bool:
    expected = hmac.new(
        secret.encode(),
        payload,
        hashlib.sha256
    ).hexdigest()
    return hmac.compare_digest(expected, signature)

簽章驗證的最佳實踐

處理時間戳與重放攻擊

許多提供者會在簽章標頭中包含時間戳。例如,標頭可能是 t=1614556800,v1=abc123...。你應該:

  1. 提取時間戳和簽章。
  2. 檢查時間戳是否在可接受的時間窗口內(例如 5 分鐘)。
  3. 使用時間戳和負載重新計算簽章。
  4. 安全地比較簽章。

這可以防止攻擊者重放舊的有效請求。

重試:優雅地處理失敗

Webhooks 是透過公共網際網路傳送的,因此失敗是不可避免的。你的端點可能離線、緩慢或傳回 500 錯誤。一個穩健的提供者會重試失敗的傳送,但如果你是提供者,你也需要在自己的端處理重試。

帶有抖動的指數退避

重試時,避免立即重試對端點造成壓力。使用指數退避:每次失敗後等待更長時間。加入抖動(隨機性)以避免當許多 Webhooks 同時失敗時出現的驚群效應。

典型的重試排程可能如下所示:

嘗試次數延遲(約)
1立即
230 秒
35 分鐘
430 分鐘
52 小時

達到最大嘗試次數後,將傳送標記為失敗並發出警報。

幂等性:安全重試的關鍵

由於重試可能導致重複傳送,你的 Webhook 處理程序必須是幂等的。這意味著多次處理同一個事件應該與處理一次具有相同的效果。

如何實現幂等性:

在提供者端實作重試

如果你正在建置 Webhook 系統,你需要一個可靠的傳送機制。以下是一個簡化的方法:

  1. 當事件發生時,將帶有負載和目標 URL 的傳送作業加入佇列。
  2. 工作人員提取作業並發送 HTTP POST,設定超時(例如 10 秒)。
  3. 如果回應狀態不是 2xx,則使用指數退避安排重試。
  4. 追蹤嘗試次數,並在達到最大值(例如 5 次)後放棄。
  5. 記錄失敗並提供使用者手動重放的方式。

使用持久性佇列(例如 Redis、RabbitMQ 或資料庫表),以便重試能在重新啟動後繼續。

綜合整理:Webhook 接收器檢查清單

建置 Webhook 接收器時,請遵循以下檢查清單:

常見問題

什麼是簽章 Webhook 負載?

簽章負載包含使用共享密鑰生成的加密簽章(通常是 HMAC-SHA256)。接收方重新計算簽章以驗證請求來自預期的發送方且未被篡改。

為什麼 Webhooks 需要重試?

網路問題、伺服器停機或暫時性錯誤可能導致 Webhook 傳送失敗。使用指數退避的重試可確保事件最終被送達,而不會壓垮接收方。

如何防止重複處理 Webhook?

使用幂等性:在負載中包含唯一事件 ID,儲存已處理的 ID,並將操作設計為幂等(例如 upsert)。這樣,即使 Webhook 被多次傳送,效果也是相同的。

如果你需要檢查或除錯 Webhook 負載,JSON Formatter 可以幫助你快速美化列印和驗證 JSON 資料。