Webhooks 解析:簽名載荷與重試機制
你剛剛整合了一個第三方支付提供商。使用者完成了一筆購買,但你的資料庫卻從未更新。為什麼?因為本應通知你應用程式的 webhook 無聲地失敗了。如果沒有正確的簽名驗證和重試邏輯,webhooks 可能會成為神秘錯誤和安全漏洞的來源。
在本文中,我們將解釋 webhooks 如何運作、為什麼必須驗證簽名載荷,以及如何設計穩健的重試機制。我們還會展示如何有效地測試和除錯 webhooks。
什麼是 Webhooks?
Webhooks 是使用者定義的 HTTP 回呼。它們允許一個系統在事件發生時將即時資料傳送給另一個系統。例如,當支付被處理時,支付提供商會向你指定的 URL 傳送一個 HTTP POST 請求,其中包含事件的詳細資訊。
與輪詢(你的應用程式反覆請求更新)不同,webhooks 會在資料可用時立即推送給你。這使得它們高效且近乎即時。
安全問題:為什麼要驗證簽名?
Webhook 端點是公開可存取的 URL。任何人都可以向它們傳送 POST 請求。如果沒有驗證,攻擊者可以偽造請求,例如在沒有實際付款的情況下將訂單標記為已付款。為了防止這種情況,提供商會用密鑰對每個載荷進行簽名。
HMAC 簽名如何運作
大多數提供商使用 HMAC(基於雜湊的訊息驗證碼)搭配 SHA-256。他們使用共享密鑰計算請求主體的雜湊,並將其包含在標頭中,例如 X-Signature。你的伺服器重新計算雜湊並與標頭進行比較。如果匹配,則請求是真實的。
以下是 Python 中的簡單範例:
import hmac
import hashlib
def verify_signature(payload, secret, signature):
expected = hmac.new(secret.encode(), payload, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, signature)
始終使用恆定時間比較函式,如 hmac.compare_digest,以避免計時攻擊。
應避免的常見錯誤
- 使用錯誤的密鑰:每個環境(測試、正式)都應有自己的密鑰。
- 未驗證時間戳:某些提供商包含時間戳以防止重放攻擊。拒絕超過幾分鐘的請求。
- 記錄密鑰:切勿在生產環境中記錄密鑰或完整簽名。
設計可靠的重試
Webhooks 透過網際網路傳送,因此失敗是不可避免的。網路中斷、伺服器重新啟動以及端點逾時。良好的 webhook 系統會使用指數退避重試失敗的傳遞。
重試通常如何運作
提供商通常會以遞增的延遲重試:1 分鐘後、5 分鐘後、30 分鐘後等,直到達到最大嘗試次數(例如 5-10 次)。如果所有重試都失敗,則該事件被標記為失敗,並可能可供手動重放。
你的端點應快速回應 2xx 狀態碼以確認收到。如果你返回非 2xx 代碼或逾時,提供商將重試。
實作冪等性
由於重試可能導致重複傳遞,你的端點必須是冪等的。使用事件 ID(包含在載荷或標頭中)來追蹤已處理的事件。在處理之前,檢查你是否已經處理過該 ID。將已處理的 ID 儲存在具有 TTL 的資料庫中。
冪等性檢查範例:
def process_event(event_id, data):
if db.exists(f"processed:{event_id}"):
return # already processed
# process data
db.setex(f"processed:{event_id}", 86400, "1") # expire after 24h
逐步建置 Webhook 接收器
- 公開 HTTPS 端點:使用 TLS 保護傳輸中的資料。
- 驗證簽名:以 401 拒絕簽名無效的請求。
- 檢查時間戳:如果提供,確保它在可接受的時間窗口內(例如 5 分鐘)。
- 解析載荷:驗證 JSON 結構和必要欄位。
- 檢查冪等性:使用事件 ID 避免重複。
- 非同步處理:立即回應 200,然後在背景作業中處理以避免逾時。
- 記錄和監控:記錄失敗並設定警報。
在本機測試 Webhooks
在開發過程中,你需要一種方式在本機機器上接收 webhooks。像 ngrok 或 localtunnel 這樣的工具會建立一個公開 URL,轉發到你的 localhost。或者,許多提供商提供 CLI 來觸發測試事件。
始終使用有效和無效的簽名測試簽名驗證。透過返回錯誤代碼並觀察提供商的重試行為來模擬重試。
比較:Webhooks 與輪詢
| 方面 | Webhooks | 輪詢 |
|---|---|---|
| 即時性 | 近乎即時 | 受間隔延遲 |
| 效率 | 僅在事件發生時傳送資料 | 即使沒有資料也頻繁請求 |
| 複雜性 | 需要端點、簽名驗證、重試 | 實作更簡單 |
| 可靠性 | 取決於重試機制 | 你控制排程 |
常見問題
什麼是簽名 webhook 載荷?
簽名載荷包含加密簽名(通常是 HMAC),允許接收方驗證請求來自預期的傳送方且未被篡改。
Webhook 應該重試多少次?
大多數提供商使用指數退避重試 5-10 次。確切次數取決於提供商的政策。你的端點應該是冪等的以處理重複。
我的 webhook 應該返回什麼 HTTP 狀態碼?
返回 2xx 狀態碼(例如 200 OK)以確認成功接收。任何其他代碼(4xx 或 5xx)表示失敗並觸發重試。
準備好測試你的 webhook 載荷了嗎?使用我們的 JSON Formatter 快速驗證和美化 JSON 載荷。