Webhook解説:署名付きペイロードとリトライ
決済ゲートウェイやCI/CDサービスを統合し、リアルタイムイベントを受信する必要が出てきましたか?Webhookは標準的なソリューションですが、落とし穴があります:認証されていないペイロード、失われるイベント、重複配信です。この記事では、Webhookの仕組み、改ざんを防ぐためのペイロード署名方法、信頼性の高い配信のためのリトライ実装について説明します。
Webhookとは?
Webhookはユーザー定義のHTTPコールバックです。アプリケーションがAPIをポーリングして更新を確認する代わりに、イベントが発生するたびにプロバイダーが指定したURLにHTTP POSTリクエストを送信します。これはより効率的で、ほぼリアルタイムの反応を可能にします。
一般的なユースケース:
- 決済通知(例:Stripe、PayPal)
- CI/CDビルドステータス(例:GitHub、GitLab)
- メッセージングプラットフォーム(例:Slack、Discord)
- CRM更新(例:Salesforce)
なぜWebhookペイロードに署名するのか?
Webhookエンドポイントは公開アクセス可能なURLです。検証がなければ、誰でも偽のペイロードを送信でき、データ破損やセキュリティ侵害につながります。共有シークレットでペイロードに署名することで、真正性と完全性が保証されます。
ほとんどのプロバイダーはSHA-256を使用したHMAC(ハッシュベースのメッセージ認証コード)を使用しています。プロバイダーは秘密鍵を使用して生のリクエストボディに対して署名を計算し、ヘッダー(例:X-Hub-Signature-256)に含めます。サーバーは署名を再計算して比較します。
署名の検証方法
手順は以下の通りです:
- 生のボディを取得:送信されたままの状態で取得します。検証前に解析や変更を加えないでください。
- 署名を抽出:ヘッダーから署名を取り出します。
- 期待される署名を計算:秘密鍵を使用してHMAC-SHA256で計算します。
- 比較:タイミング攻撃を防ぐために定数時間関数を使用します。
Node.jsの例:
const crypto = require('crypto');
function verifySignature(payload, signature, secret) {
const hmac = crypto.createHmac('sha256', secret);
const digest = 'sha256=' + hmac.update(payload).digest('hex');
return crypto.timingSafeEqual(Buffer.from(digest), Buffer.from(signature));
}
Pythonの場合:
import hmac
import hashlib
def verify_signature(payload, signature, secret):
expected = hmac.new(secret.encode(), payload, hashlib.sha256).hexdigest()
return hmac.compare_digest(f'sha256={expected}', signature)
情報漏洩を避けるために、常に定数時間比較を使用してください。
信頼性のためのリトライ実装
ネットワークは失敗し、サーバーは再起動し、デプロイは発生します。堅牢なWebhookシステムは失敗した配信をリトライする必要があります。プロバイダーは通常指数バックオフでリトライしますが、受信側でもリトライを処理する必要があります。
リトライ戦略
- 指数バックオフ: リトライ間に1秒、2秒、4秒、8秒など待機します。
- 最大試行回数: 無限ループを避けるためにリトライを制限します(例:5回)。
- デッドレターキュー: 最大試行後、手動検査のためにイベントを保存します。
- 冪等性: 同じイベントを複数回処理しても重複した副作用が発生しないようにします。
冪等性キー
多くのプロバイダーは一意のイベントID(例:X-Event-ID)を含めています。処理済みIDをデータベースやキャッシュに保存して重複をスキップします。例えば、RedisとTTLを使用して確認済みIDを追跡します。
比較:ポーリング vs Webhook
| 側面 | ポーリング | Webhook |
|---|---|---|
| レイテンシ | 間隔に依存 | ほぼリアルタイム |
| サーバー負荷 | 高い(常時リクエスト) | 低い(イベント時のみ) |
| 複雑さ | 実装が簡単 | エンドポイント、セキュリティ、リトライが必要 |
| 信頼性 | ポーリング間のイベントを見逃す | エンドポイントダウン時は見逃す可能性;リトライが助ける |
Webhookコンシューマーのベストプラクティス
- 迅速に応答: 数秒以内に2xxを返し、非同期で処理します。
- 署名を検証: 処理前に必ず検証します。
- すべてをログ: デバッグ用に生のペイロードとヘッダーを保持します。
- HTTPSを使用: 平文HTTPでWebhookを受け入れないでください。
- 失敗を監視: 繰り返しのリトライ失敗に対してアラートを設定します。
FAQ
Webhook署名とは何ですか?
Webhook署名は、共有シークレットで作成されたペイロードのHMACハッシュです。受信者がリクエストが期待された送信者から来て改ざんされていないことを検証できるようにします。
失敗したWebhookを何回リトライすべきですか?
普遍的な数はありませんが、指数バックオフで3〜5回のリトライが一般的です。その後、手動レビューのためにイベントをデッドレターキューにログします。
HTTPSなしでWebhookを使用できますか?
技術的には可能ですが、非常に安全ではありません。ペイロードを暗号化し、中間者攻撃を防ぐために常にHTTPSを使用してください。
Webhookエンドポイントをテストする準備はできましたか?JSON Formatterを使用して、ペイロードを迅速に検査および検証しましょう。