Webhooks 詳解:簽名負載與重試策略
你剛剛整合了支付閘道或 CI/CD 服務,現在需要接收即時事件。你設定了一個端點,但很快面臨兩個關鍵問題:如何確保請求確實來自該服務?以及如果事件觸發時你的伺服器宕機了怎麼辦?本文透過解釋簽名負載和重試策略來回答這兩個問題——它們是可靠 Webhooks 的骨幹。
什麼是 Webhooks?
Webhooks 是使用者定義的 HTTP 回呼。當來源系統中發生事件(例如新的提交、支付成功)時,它會向你提供的 URL 發送 HTTP POST 請求。與輪詢不同,Webhooks 近即時推送資料,減少延遲和伺服器負載。然而,這種推送模型帶來了挑戰:驗證真實性和處理傳遞失敗。
為什麼簽名負載很重要
任何人都可以向你的端點發送 HTTP 請求。如果沒有驗證,攻擊者可以偽造事件,導致未授權的操作。簽名負載透過包含基於請求主體使用共享密鑰計算的加密簽名來解決此問題。接收方重新計算簽名並進行比較,確保負載未被篡改且來自預期的發送方。
HMAC 簽名如何運作
大多數服務使用 HMAC-SHA256。發送方使用密鑰對負載進行雜湊計算來建立簽名,並將其包含在標頭中(例如 X-Signature)。接收方執行相同操作,並使用常數時間函數進行比較以防止計時攻擊。
逐步指南:驗證簽名負載
- 從標頭中提取簽名(例如
X-Signature)。 - 取得原始請求主體作為字串或緩衝區——不要先解析它,因為格式變更會破壞簽名。
- 使用你的密鑰計算預期簽名(使用 HMAC-SHA256)。
- 使用常數時間比較函數比較簽名。
- 如果它們不匹配,則以 HTTP 401 拒絕請求。
以下是使用 Express 的 Node.js 範例:
const crypto = require('crypto');
const express = require('express');
const app = express();
app.post('/webhook', express.raw({type: 'application/json'}), (req, res) => {
const signature = req.headers['x-signature'];
const secret = process.env.WEBHOOK_SECRET;
const payload = req.body; // raw buffer
const expected = crypto
.createHmac('sha256', secret)
.update(payload)
.digest('hex');
if (!crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected))) {
return res.status(401).send('Invalid signature');
}
// Process the event
const event = JSON.parse(payload);
// ... handle event
res.status(200).send('OK');
});
注意: 始終使用原始主體進行驗證。解析 JSON 的中間件會改變負載,導致簽名不匹配。
設計重試策略
即使有簽名負載,網路問題或伺服器停機也可能導致傳遞失敗。穩健的重試策略確保事件不會丟失。關鍵考慮因素:
1. 快速確認
一旦驗證簽名並將事件排入佇列進行處理,就立即回應 2xx 狀態碼。這可以防止發送方超時並進行不必要的重試。
2. 冪等性
重試可能會多次傳遞同一事件。使用冪等鍵(通常包含在負載或標頭中)進行去重。儲存已處理的事件 ID 並忽略重複項。
3. 指數退避與抖動
如果你的端點返回錯誤(非 2xx),發送方應以遞增的延遲進行重試。指數退避(例如 1 秒、2 秒、4 秒、8 秒)可在停機期間減少負載。添加隨機抖動可防止驚群問題。
典型重試排程:
| 嘗試次數 | 延遲 |
|---|---|
| 1 | 立即 |
| 2 | 1 分鐘 |
| 3 | 5 分鐘 |
| 4 | 30 分鐘 |
| 5 | 2 小時 |
4. 死信佇列
在最大重試次數後,將事件移至死信佇列以供手動檢查。這可以防止無限重試和資料丟失。
5. 監控與警報
追蹤傳遞成功率、延遲和失敗原因。設定警報以監控失敗峰值,及早發現問題。
常見陷阱與最佳實踐
- 始終驗證簽名——切勿盲目信任傳入的資料。
- 使用 HTTPS 保護傳輸中的負載。
- 定期輪換密鑰,並在輪換期間支援多個活動密鑰。
- 記錄 webhook 事件(敏感資料需遮蔽)以供除錯。
- 使用模擬 webhook 傳遞的工具測試範例負載。
常見問題
什麼是 webhooks 中的簽名負載?
簽名負載包含加密簽名(通常是 HMAC),讓接收方能夠驗證請求的真實性和完整性。
webhook 應該重試多少次?
沒有固定次數;常見做法是使用指數退避進行 3 到 10 次重試。之後,事件通常會發送到死信佇列。
我可以不使用簽名嗎?
可以,但強烈不建議。沒有簽名,你的端點容易受到偽造請求和資料洩露的攻擊。
準備好測試你的 webhook 負載了嗎?使用我們的 JSON Formatter 快速檢查和驗證 JSON 負載。