Webhooks 詳解:簽名載荷與重試策略
你剛剛整合了一個會發送 webhooks 的第三方服務,在測試環境中一切正常。但在生產環境中,你偶爾會注意到漏掉的事件、重複的傳遞,或者更糟——惡意攻擊者向你的端點發送偽造載荷。Webhooks 概念簡單但實作起來卻很棘手。本文說明如何透過簽名載荷保護 webhooks,並實作穩健的重試策略,讓你能信任事件驅動的整合。
什麼是 Webhooks 以及它們為何重要
Webhooks 是由特定事件觸發的使用者定義 HTTP 回呼。當事件發生時——例如付款成功或新使用者註冊——來源服務會向你提供的 URL 發送 HTTP POST 請求。這種推送式模型比輪詢更有效率,能降低延遲和伺服器負載。然而,它也帶來了挑戰:驗證真實性和處理傳遞失敗。
使用簽名載荷保護 Webhooks
若沒有驗證,任何人都可以向你的 webhook 端點發送請求。簽名載荷透過包含加密簽章來解決這個問題,證明請求來自受信任的發送方。
HMAC 簽章如何運作
大多數供應商使用 HMAC(雜湊訊息鑑別碼)搭配共享密鑰。發送方使用密鑰計算載荷的雜湊值,並將其包含在標頭中(例如 X-Signature)。你的伺服器重新計算雜湊值並進行比較。如果匹配,則請求是真實的。
// 範例:在 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');
return crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(digest));
}
重要: 始終使用時間安全的比較以防止時間攻擊。此外,請針對原始請求主體驗證簽章,而不是解析後的 JSON 物件,因為解析可能會改變載荷。
簽章驗證的最佳實踐
- 使用 HTTPS: 始終透過 TLS 提供 webhooks 以防止竊聽。
- 輪換密鑰: 定期輪換你的簽章密鑰,並在輪換期間支援多個活躍密鑰。
- 驗證時間戳: 在簽章中包含時間戳以防止重播攻擊。拒絕超過幾分鐘的請求。
- 記錄失敗: 監控簽章驗證失敗以偵測攻擊或錯誤配置。
實作可靠的重試
即使有簽名載荷,網路問題或暫時性中斷也可能導致 webhook 傳遞失敗。穩健的重試策略確保事件最終被傳遞,同時不會壓垮你的系統。
指數退避與抖動
立即重試可能會加劇擁塞。相反,使用指數退避:每次重試之間等待更長時間。加入抖動(隨機變化)以避免當許多 webhooks 同時失敗時出現驚群效應。
// 範例:具有指數退避和抖動的重試排程
function getRetryDelay(attempt) {
const base = 1000; // 1 秒
const max = 30000; // 30 秒
const delay = Math.min(base * Math.pow(2, attempt), max);
return delay + Math.random() * 1000; // 加入最多 1 秒的抖動
}
冪等性:處理重複
重試可能導致重複傳遞。你的 webhook 處理常式必須是冪等的——多次處理相同事件應與處理一次具有相同效果。使用載荷中的事件 ID 來追蹤已處理的事件。
- 將已處理的事件 ID 儲存在具有 TTL 的資料庫中。
- 在處理之前,檢查事件 ID 是否已存在;如果存在,立即返回成功回應。
- 設計操作為冪等(例如,使用 upsert 而不是 insert)。
快速回應並確認
你的 webhook 端點應盡快回應 2xx 狀態碼——理想情況下在幾秒內。如果處理需要更長時間,將事件排入佇列進行非同步處理並返回 202 Accepted。這可以防止逾時和不必要的重試。
比較:Webhook 與輪詢
| 方面 | Webhooks | 輪詢 |
|---|---|---|
| 延遲 | 近乎即時 | 取決於間隔 |
| 伺服器負載 | 低(僅推送) | 高(持續請求) |
| 複雜度 | 較高(需要重試、安全性) | 較低 |
| 可靠性 | 需要重試邏輯 | 本質上可靠 |
逐步指南:建立安全的 Webhook 接收器
- 設定 HTTPS 端點 並使用有效憑證。
- 提取簽章標頭(例如
X-Signature)和時間戳。 - 驗證簽章 使用共享密鑰和原始主體。
- 檢查時間戳 以拒絕舊請求(例如超過 5 分鐘)。
- 解析載荷 並檢查重複的事件 ID。
- 非同步處理事件(如有需要),並回應 200 OK。
- 記錄事件 以供稽核和除錯。
常見問題
什麼是簽名 webhook 載荷?
簽名載荷包含加密簽章(通常是 HMAC),允許接收方驗證請求來自受信任的來源且未被篡改。
我應該重試失敗的 webhook 多少次?
大多數系統使用指數退避重試 3–5 次。之後,事件通常被標記為失敗,可能需要手動介入或死信佇列處理。
我可以不使用 HTTPS 使用 webhooks 嗎?
強烈不建議。沒有 HTTPS,載荷和簽章可能被攔截或修改。始終為 webhook 端點使用 TLS。
在除錯 webhook 載荷或日誌時,正確格式化 JSON 至關重要。使用我們的 JSON Formatter 快速美化列印和驗證 JSON 載荷。