Webhook 詳解:簽章酬載與重試機制
為什麼 Webhook 既強大又脆弱
Webhook 驅動了即時整合:付款通知、CI/CD 觸發、聊天訊息。但它們帶來兩大挑戰:安全性(如何確認請求來自預期的發送方?)以及可靠性(如果接收方離線怎麼辦?)。本文將說明如何透過簽章酬載與重試策略來解決這兩個問題。
什麼是 Webhook?
Webhook 是當事件發生時,由提供者發送給消費者的 HTTP POST 請求。與輪詢不同,Webhook 以近乎即時的方式推送資料,降低延遲與伺服器負載。提供者必須確保請求的真實性;消費者則必須可靠地處理它。
使用簽章酬載保護 Webhook
簽章酬載使用密鑰對請求主體建立密碼學簽章(通常為 HMAC-SHA256)。接收方重新計算簽章並與標頭中的簽章進行比對。若兩者相符,則酬載是真實且未被竄改的。
如何產生與驗證簽章
以下是典型的流程:
- 提供者與消費者共享一組密鑰(例如透過儀表板)。
- 提供者計算
HMAC-SHA256(secret, payload),並將其放入如X-Signature的標頭中發送。 - 消費者讀取原始主體,計算相同的 HMAC,並使用恆定時間函式進行比對。
Node.js 範例:
const crypto = require('crypto');
function verifySignature(secret, payload, signature) {
const hmac = crypto.createHmac('sha256', secret);
hmac.update(payload);
const digest = hmac.digest('hex');
return crypto.timingSafeEqual(Buffer.from(digest), Buffer.from(signature));
}
務必使用恆定時間比對以防止時序攻擊。切勿在驗證前解析主體——請使用原始主體中介軟體。
實作重試以確保可靠傳遞
即使有簽章,網路故障或暫時性中斷仍可能導致 Webhook 傳遞失敗。穩健的重試機制可確保最終送達。
重試策略
- 指數退避:每次重試之間等待更長時間(例如 1 秒、2 秒、4 秒、8 秒)。
- 最大嘗試次數:限制重試次數(例如 5 次)以避免無限迴圈。
- 死信佇列:達到最大嘗試次數後,儲存事件以供人工檢查。
- 幂等性:包含唯一的事件 ID,讓消費者能忽略重複事件。
Python 重試邏輯範例:
import time
import requests
def send_webhook(url, payload, max_retries=5):
for attempt in range(max_retries):
try:
response = requests.post(url, json=payload, timeout=5)
if response.status_code == 200:
return True
except requests.RequestException:
pass
time.sleep(2 ** attempt) # exponential backoff
return False
Webhook 消費者的最佳實踐
- 快速回應 2xx;如有需要,以非同步方式處理。
- 在進行任何處理之前先驗證簽章。
- 使用幂等鍵來處理重複傳遞。
- 記錄所有傳入的 Webhook 以便除錯。
- 監控失敗情況,並在重複發生錯誤時發出警報。
比較:輪詢 vs Webhook
| 面向 | 輪詢 | Webhook |
|---|---|---|
| 延遲 | 高(基於間隔) | 低(即時) |
| 伺服器負載 | 持續請求 | 僅在事件發生時 |
| 複雜度 | 簡單 | 需要重試與安全性機制 |
| 使用情境 | 小規模、不頻繁更新 | 即時整合 |
常見問題
什麼是簽章 Webhook 酬載?
簽章酬載包含密碼學簽章(例如 HMAC),讓接收方能驗證請求來自可信任的來源,且未被竄改。
失敗的 Webhook 應該重試幾次?
沒有通用的次數,但通常以指數退避重試 3 至 5 次。之後,記錄事件以供人工審查。
我可以使用 JWT 取代 HMAC 來簽署 Webhook 嗎?
可以,有些提供者使用 JWT。原理相同:在信任酬載之前,先驗證權杖的簽章與聲明。
需要檢查 Webhook 酬載或日誌嗎?試試我們的 JSON Formatter,快速美化並驗證 JSON 酬載。