Webhook解説:署名付きペイロードとリトライ
決済プロバイダーのWebhookを統合して取引更新を受け取ったばかりだとします。テストではすべてうまくいきますが、本番環境ではトラフィックスパイク時にイベントが欠落したり、プロバイダーがリトライしたときに二重請求が発生したりすることに気づきます。Webhookは概念としてはシンプルですが、実際には扱いが難しいものです。ネットワーク境界を越え、遅延、重複、なりすましの可能性があります。この記事では、署名付きペイロードとスマートなリトライ戦略を使用して、堅牢なWebhookコンシューマーとプロデューサーを構築する方法を説明します。
Webhookとは?
Webhookは、イベントによってトリガーされるユーザー定義のHTTPコールバックです。ソースシステムで何かが起こると(例:新規注文)、指定したURLにHTTP POSTリクエストを送信します。ポーリングとは異なり、Webhookはほぼリアルタイムでデータをプッシュするため、レイテンシとサーバー負荷を削減できます。
ただし、Webhookには課題があります。リクエストが本当に期待される送信者からのものかを確認し、障害を適切に処理し、重複処理を避ける必要があります。
署名付きペイロードでWebhookを保護する
検証がないと、誰でも偽のリクエストをWebhookエンドポイントに送信できます。署名付きペイロードは、検証可能な暗号署名を含めることでこれを解決します。
HMAC署名の仕組み
送信者は、共有シークレットとリクエストボディを使用してハッシュベースのメッセージ認証コード(HMAC)を計算します。署名はヘッダー(例:X-Signature)で送信されます。受信側でHMACを再計算し、比較します。
// Node.jsの例:Webhook署名の検証
const crypto = require('crypto');
function verifySignature(payload, signature, secret) {
const expected = crypto
.createHmac('sha256', secret)
.update(payload)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expected)
);
}
タイミング攻撃を防ぐために、常にtimingSafeEqualのような定数時間比較関数を使用してください。
タイムスタンプを含める
リプレイ攻撃を防ぐために、署名付きペイロードにタイムスタンプを含め、数分(例:5分)より古いリクエストを拒否します。タイムスタンプは署名対象データの一部である必要があります。
信頼性の高いリトライの実装
ネットワークの問題や一時的なサーバーエラーにより、Webhookの配信が失敗する可能性があります。堅牢な送信者は、指数バックオフを使用して失敗した配信をリトライします。
リトライ戦略
- 指数バックオフ:各リトライ間の待機時間を長くします(例:1秒、2秒、4秒、8秒...)。
- 最大リトライ回数:無限ループを避けるために制限を設定します(例:5回)。
- デッドレターキュー:最大リトライ後、手動検査のためにイベントを保存します。
- 冪等性:受信者が重複排除できるように一意のイベントIDを含めます。
例:Pythonでのバックオフ付きリトライ
import time
import requests
from requests.exceptions import RequestException
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 < 500:
return True # 成功またはクライアントエラー(リトライしない)
except RequestException:
pass
time.sleep(2 ** attempt) # 指数バックオフ
return False
Webhookコンシューマーのベストプラクティス
- 迅速に応答する:処理前に2xxステータスで受信を確認します。作業は非同期でキューに入れます。
- 署名を検証する:ペイロードを信頼する前に必ず署名を検証します。
- 重複を処理する:イベントIDを使用して冪等な処理を保証します。
- すべてをログに記録する:デバッグのために受信したWebhookのログを保持します。
比較:Webhook vs ポーリング
| 側面 | Webhook | ポーリング |
|---|---|---|
| レイテンシ | ほぼリアルタイム | 間隔に依存 |
| サーバー負荷 | 低い(プッシュのみ) | 高い(頻繁なリクエスト) |
| 複雑さ | 高い(エンドポイント、セキュリティが必要) | 低い |
| 信頼性 | リトライが必要 | 本質的にリトライされる |
FAQ
署名付きWebhookペイロードとは何ですか?
署名付きペイロードには、受信者がリクエストの真正性と整合性を検証できる暗号署名(通常はHMAC)が含まれています。
失敗したWebhookを何回リトライすべきですか?
一般的な方法は、指数バックオフで3〜5回のリトライですが、SLAとイベントの重要度によって異なります。
HTTPSなしでWebhookを使用できますか?
いいえ、ペイロードと署名の傍受を防ぐために常にHTTPSを使用してください。平文のHTTPは機密データを露出します。
Webhookペイロードをデバッグする際、JSONを素早く整形する必要があるかもしれません。JSONレスポンスを整形して検証するには、JSON Formatterをお試しください。