Webhook 詳解:簽章載荷與重試機制

Backend2026-11-08TryQuickToolBox

你剛剛整合了支付供應商的 Webhook。文件上寫著「當支付成功時,我們會向你的端點發送 POST 請求」。聽起來很簡單,對吧?然後你注意到重複的事件、遺漏的通知,以及關於偽造請求的安全警報。Webhook 概念簡單,但在生產環境中卻很棘手。本文解釋如何正確地實作 Webhook,使用簽章載荷和重試機制,讓你建構可靠且安全的整合。

什麼是 Webhook?

Webhook 是一種反向 API:你的應用程式不需要輪詢服務來取得更新,而是由服務透過 HTTP POST 將事件推送到你的端點。這種方式高效且近乎即時。常見用途包括支付通知、CI/CD 觸發和聊天整合。

但 Webhook 帶來了挑戰:你如何知道請求真的來自供應商?如果你的伺服器宕機了怎麼辦?如果同一個事件到達兩次怎麼辦?讓我們逐一解決這些問題。

使用簽章載荷保護 Webhook

任何人都可以向你的端點發送 HTTP 請求。如果沒有驗證,攻擊者可以偽造事件,導致欺詐或資料損壞。標準的解決方案是使用共享密鑰對載荷進行簽章。

HMAC 簽章如何運作

供應商和你共享一個密鑰。對於每個事件,供應商使用該密鑰計算請求主體的 HMAC(基於雜湊的訊息驗證碼),並在標頭中發送簽章(例如 X-Signature)。你在自己這邊重新計算 HMAC 並進行比較。如果匹配,則請求是真實且未被篡改的。

以下是使用 Flask 的 Python 最小範例:

import hmac
import hashlib
from flask import Flask, request, abort

app = Flask(__name__)
SECRET = b'your_shared_secret'

@app.route('/webhook', methods=['POST'])
def webhook():
    signature = request.headers.get('X-Signature')
    if not signature:
        abort(400, 'Missing signature')
    body = request.get_data()
    expected = hmac.new(SECRET, body, hashlib.sha256).hexdigest()
    if not hmac.compare_digest(expected, signature):
        abort(401, 'Invalid signature')
    # Process the event
    return '', 200

始終使用 hmac.compare_digest 進行恆定時間比較,以防止時序攻擊。此外,在解析 JSON 主體之前驗證簽章,以避免不必要的處理。

簽章的最佳實踐

透過重試處理失敗

從供應商的角度來看,Webhook 是發後即忘的,但你的端點可能暫時不可用。為確保傳遞,供應商實施重試策略。作為接收方,你必須為重試和重複做好準備。

供應商使用的重試策略

大多數供應商使用指數退避進行重試:例如,立即重試,然後在 1 分鐘、5 分鐘、30 分鐘後重試,直到達到最大嘗試次數(通常為 5-10 次)。有些還允許你配置重試時間表。具體行為因供應商而異,因此請閱讀供應商的文件。

為重試設計你的端點

你的端點應該:

Python 中冪等性檢查的範例:

processed_events = set()  # In production, use a database or cache

@app.route('/webhook', methods=['POST'])
def webhook():
    # ... signature verification ...
    event = request.json
    event_id = event['id']
    if event_id in processed_events:
        return '', 200  # Already processed
    # Process event
    processed_events.add(event_id)
    return '', 200

測試你的 Webhook 實作

在上線之前,徹底測試你的端點:

  1. 模擬簽章請求:編寫單元測試,生成有效和無效的簽章。
  2. 測試重試場景:模擬失敗並確保你的端點處理重複事件。
  3. 使用供應商工具:許多供應商提供 CLI 或儀表板來發送測試事件。
  4. 監控和記錄:記錄傳入的事件(不含敏感資料),並為簽章失敗或高錯誤率設置警報。

比較:輪詢 vs Webhook

方面輪詢Webhook
延遲取決於間隔(例如幾分鐘)近乎即時
伺服器負載高(頻繁請求)低(僅在事件發生時)
複雜性實作簡單需要簽章驗證和重試處理
可靠性你控制重試供應商控制重試;你必須處理重複

常見問題

如何驗證 Webhook 簽章?

使用共享密鑰計算原始請求主體的 HMAC,並使用恆定時間比較函數將其與簽章標頭進行比較。在處理載荷之前始終進行驗證。

我的端點應該返回什麼來確認 Webhook?

一旦你收到事件並將其排入佇列進行處理,就返回 2xx 狀態碼(例如 200 OK)。避免在請求處理程序中進行長時間處理,以防止逾時。

如何處理重複的 Webhook 傳遞?

使用事件載荷中的冪等鍵(或唯一事件 ID)來追蹤已處理的事件。將這些 ID 儲存在資料庫或快取中,如果 ID 之前已見過,則跳過處理。

準備好測試你的 Webhook 載荷了嗎?使用我們的 JSON Formatter 來美化列印和驗證傳入 Webhook 的 JSON 主體,讓除錯更容易。