Webhooks 詳解:簽章載荷與重試機制
你剛剛整合了支付閘道或 CI/CD 服務,現在需要接收即時事件。Webhooks 是標準解決方案,但伴隨著一些陷阱:未經驗證的載荷、遺失的事件以及重複傳遞。本文將說明 webhooks 的運作方式、如何簽署載荷以防止竄改,以及如何實作重試以確保可靠傳遞。
什麼是 Webhooks?
Webhook 是一種使用者定義的 HTTP 回呼。每當事件發生時,提供者會向你指定的 URL 發送 HTTP POST 請求,而不是讓你的應用程式輪詢 API 來取得更新。這種方式更有效率,並能實現近乎即時的反應。
常見的使用案例包括:
- 付款通知(例如 Stripe、PayPal)
- CI/CD 建置狀態(例如 GitHub、GitLab)
- 訊息平台(例如 Slack、Discord)
- CRM 更新(例如 Salesforce)
為什麼要簽署 Webhook 載荷?
Webhook 端點是公開可存取的 URL。若未經驗證,任何人都可以發送偽造的載荷,導致資料損毀或安全漏洞。使用共享密鑰簽署載荷可確保真實性和完整性。
大多數提供者使用 HMAC(雜湊訊息鑑別碼)搭配 SHA-256。提供者使用密鑰對原始請求主體計算簽章,並將其包含在標頭中(例如 X-Hub-Signature-256)。你的伺服器重新計算簽章並進行比對。
如何驗證簽章
以下是逐步指南:
- 取得原始主體,完全按照發送的內容。在驗證前請勿解析或修改它。
- 從標頭中提取簽章。
- 使用你的密鑰和 HMAC-SHA256 計算預期簽章。
- 使用恆定時間函式進行比較,以防止計時攻擊。
Node.js 範例:
const crypto = require('crypto');
function verifySignature(payload, signature, secret) {
const hmac = crypto.createHmac('sha256', secret);
const digest = 'sha256=' + hmac.update(payload).digest('hex');
return crypto.timingSafeEqual(Buffer.from(digest), Buffer.from(signature));
}
Python 範例:
import hmac
import hashlib
def verify_signature(payload, signature, secret):
expected = hmac.new(secret.encode(), payload, hashlib.sha256).hexdigest()
return hmac.compare_digest(f'sha256={expected}', signature)
始終使用恆定時間比較,以避免洩漏資訊。
實作重試以確保可靠性
網路會故障、伺服器會重新啟動,部署也會發生。一個穩健的 webhook 系統必須重試失敗的傳遞。提供者通常會使用指數退避進行重試,但你也應該在接收端處理重試。
重試策略
- 指數退避: 在重試之間等待 1 秒、2 秒、4 秒、8 秒等。
- 最大嘗試次數: 限制重試次數以避免無限迴圈(例如 5 次嘗試)。
- 死信佇列: 在達到最大嘗試次數後,儲存事件以供手動檢查。
- 冪等性: 確保多次處理相同事件不會導致重複的副作用。
冪等鍵
許多提供者會包含唯一的事件 ID(例如 X-Event-ID)。將已處理的 ID 儲存在資料庫或快取中,以跳過重複項。例如,使用 Redis 搭配 TTL 來追蹤已見過的 ID。
比較:輪詢 vs Webhooks
| 面向 | 輪詢 | Webhooks |
|---|---|---|
| 延遲 | 取決於間隔 | 近乎即時 |
| 伺服器負載 | 高(持續請求) | 低(僅在事件發生時) |
| 複雜度 | 易於實作 | 需要端點、安全性、重試 |
| 可靠性 | 可能遺漏輪詢之間的事件 | 若端點離線可能遺漏;重試有助於改善 |
Webhook 消費者的最佳實踐
- 快速回應: 在幾秒內返回 2xx;以非同步方式處理。
- 驗證簽章: 在處理前務必驗證。
- 記錄所有內容: 保留原始載荷和標頭以供除錯。
- 使用 HTTPS: 絕不接受透過純 HTTP 的 webhooks。
- 監控失敗: 為重複的重試失敗設定警示。
常見問題
什麼是 webhook 簽章?
Webhook 簽章是使用共享密鑰建立的載荷 HMAC 雜湊。它允許接收方驗證請求來自預期的發送方,且未被竄改。
我應該重試失敗的 webhook 多少次?
沒有通用的數字,但使用指數退避進行 3–5 次重試是常見做法。之後,將事件記錄到死信佇列以供手動審查。
我可以不使用 HTTPS 來使用 webhooks 嗎?
技術上可以,但非常不安全。始終使用 HTTPS 來加密載荷並防止中間人攻擊。
準備好測試你的 webhook 端點了嗎?使用我們的 JSON Formatter 快速檢查和驗證載荷。