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

Backend2026-11-02TryQuickToolBox

為何 Webhook 需要簽名與重試

你已經建立了一個端點來接收來自支付供應商或 SaaS 平台的 Webhook。在測試環境中一切正常,但在生產環境中你會面臨兩個問題:如何確認請求真的來自供應商?以及當你的伺服器宕機或回應緩慢時會發生什麼?沒有簽名載荷,任何人都可以偽造請求。沒有重試,一次網路波動就可能永久遺失一個事件。

本指南說明如何正確實作這兩者。我們將涵蓋 HMAC 簽名、驗證步驟、重試策略與冪等性——可靠處理 Webhook 的三大支柱。

Webhook 簽名如何運作

Webhook 簽名是一種密碼學雜湊,用以證明載荷是由知道共享密鑰的人所建立。大多數供應商使用 HMAC-SHA256。發送方對原始請求主體(有時包含時間戳)計算簽名,並將其放入標頭中。接收方重新計算並進行比較。

典型流程如下:

  1. 供應商與消費者在供應商的控制面板中設定共享密鑰。
  2. 供應商建立載荷,計算 HMAC-SHA256(secret, payload),並將其放入如 X-Signature 的標頭中發送。
  3. 消費者接收請求,讀取原始主體,重新計算 HMAC,並使用常數時間函數進行比較。
  4. 若匹配,則處理事件;否則以 401 拒絕。

重要: 始終使用接收到的原始請求主體,而非解析後再重新序列化的版本。JSON 序列化可能改變空白與鍵的順序,導致簽名失效。

加入時間戳以防止重放攻擊

僅有簽名載荷可能被攻擊者擷取有效請求後重放。為緩解此問題,在簽名資料中包含時間戳,並拒絕超過幾分鐘的請求。例如:

signed_payload = timestamp + "." + raw_body
signature = HMAC-SHA256(secret, signed_payload)

消費者檢查時間戳是否在可接受窗口內(例如 5 分鐘),以及簽名是否匹配。

實作簽名驗證

讓我們看一個使用 Flask 的 Python 具體範例。我們將驗證 X-Signature 標頭中發送的簽名。

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(401)

    raw_body = request.get_data()
    expected = hmac.new(SECRET, raw_body, hashlib.sha256).hexdigest()

    if not hmac.compare_digest(expected, signature):
        abort(401)

    # 處理事件
    event = request.json
    # ... 處理事件 ...
    return '', 200

重點:

設計重試策略

即使有簽名,Webhook 仍可能失敗:你的伺服器可能宕機、返回 500 或超時。供應商通常使用指數退避進行重試。作為消費者,你也應優雅地處理重試。

需考慮以下幾點:

大多數供應商在非 2xx 回應或超時時重試,通常持續長達 24 小時,間隔逐漸增加。請查閱供應商文件了解具體細節。

範例:冪等事件處理器

使用資料庫表追蹤已處理的事件 ID。以下是一個簡化模式:

def handle_event(event_id, payload):
    if db.exists('processed_events', event_id):
        return  # 已處理
    # 處理載荷
    db.insert('processed_events', event_id)
    # ... 實際工作 ...

對於高吞吐量,在事件 ID 上使用唯一約束並捕捉重複鍵錯誤。

比較:簽名與未簽名 Webhook

方面簽名載荷未簽名載荷
安全性驗證發送方真實性任何人都可以偽造請求
重放保護可透過時間戳實現無
實作工作量中等(HMAC + 時間戳檢查)低
建議用於所有生產 Webhook僅限內部測試

常見陷阱與最佳實踐

常見問題

什麼是簽名 Webhook 載荷?

簽名載荷包含密碼學簽名(通常為 HMAC),讓接收方驗證請求來自可信來源且未被篡改。

如何處理 Webhook 重試而不重複動作?

透過追蹤事件 ID 使處理器具冪等性。在處理前,檢查 ID 是否已處理;若是,則返回成功而不重複工作。

我可以使用沒有簽名的 Webhook 嗎?

不建議用於生產環境。沒有簽名,你無法區分合法請求與偽造請求。當可用時,始終驗證簽名。

在除錯 Webhook 載荷或分析伺服器日誌時,JSON Formatter 可協助你快速檢查與驗證 JSON 結構。