Webhook解説:署名付きペイロードと信頼性の高いリトライ
サードパーティの決済ゲートウェイやCI/CDサービスを統合し、リアルタイム通知が必要になったとします。Webhookがその答えですが、適切なセキュリティと信頼性がなければ、フラストレーションの種になりかねません。攻撃者はリクエストを偽装でき、ネットワークの不具合でイベントを見逃す可能性もあります。このガイドでは、署名付きペイロードでWebhookを保護し、堅牢なリトライメカニズムを実装する方法を解説します。
Webhookとは?
Webhookは、特定のイベントによってトリガーされるユーザー定義のHTTPコールバックです。イベントが発生すると(例:決済成功)、送信元サービスは指定したURLにイベントデータを含むHTTP POSTリクエストを送信します。このプッシュモデルはポーリングよりも効率的で、リアルタイムの反応を可能にします。
しかし、Webhookには2つの課題があります:真正性(リクエストが期待される送信者からのものであることを保証)と信頼性(イベントを見逃さないことを保証)です。
署名付きペイロードでWebhookを保護する
署名付きペイロードは、送信者と受信者の間で共有される秘密鍵を使用して暗号署名を作成します。送信者はこの署名をヘッダーに含め、受信者はそれを検証してペイロードの整合性と送信元を確認します。
HMAC署名の仕組み
HMAC(Hash-based Message Authentication Code)が標準的なアプローチです。送信者は共有秘密鍵を使用してリクエストボディのHMACを計算し、X-Signatureなどのヘッダーに含めます。受信者はHMACを再計算して比較します。
// 例:Node.jsで署名を生成
const crypto = require('crypto');
function generateSignature(payload, secret) {
return crypto
.createHmac('sha256', secret)
.update(JSON.stringify(payload))
.digest('hex');
}
// 受信側での検証:
function verifySignature(payload, secret, receivedSignature) {
const expected = generateSignature(payload, secret);
// タイミング攻撃を防ぐため、タイミングセーフな比較を使用
return crypto.timingSafeEqual(
Buffer.from(expected, 'hex'),
Buffer.from(receivedSignature, 'hex')
);
}
重要なポイント:
- Webhookエンドポイントごとに強力でユニークな秘密鍵を必ず使用する。
- 情報漏洩を避けるため、タイミングセーフな関数で署名を比較する。
- リプレイ攻撃を防ぐため、署名にタイムスタンプを含める(例:
timestamp + payloadに署名)。
追加のセキュリティ対策
- HTTPS:転送中のデータを暗号化するため、常にHTTPSを使用する。
- IP許可リスト:プロバイダーがIP範囲を公開している場合、受信リクエストをそれらのIPに制限する。
- ペイロードサイズ制限:DoSを軽減するため、過度に大きなペイロードを拒否する。
信頼性の高いリトライの実装
ネットワークは信頼できません。Webhookはタイムアウト、サーバーエラー、一時的な障害により失敗する可能性があります。堅牢なリトライ戦略により、システムや送信者に過負荷をかけずに最終的な配信を保証します。
リトライのベストプラクティス
- 指数バックオフ:リトライ間の遅延を増やす(例:1秒、2秒、4秒、8秒...)。
- 最大リトライ回数:無限ループを避けるため、試行回数に上限を設ける(例:5回)。
- デッドレターキュー:リトライを使い果たした後、失敗したイベントを手動検査用に保存する。
- 冪等性:同じイベントを複数回処理しても、重複した副作用が発生しないようにする。
実践的な冪等性
ペイロードに一意のイベントIDを含めます。受信側では、処理済みのIDを(データベースやキャッシュで)追跡し、重複を無視します。例えば:
// 冪等な処理の擬似コード
if (eventStore.exists(event.id)) {
return 200; // すでに処理済み
}
// イベントを処理...
eventStore.save(event.id);
失敗を適切に処理する
受信を確認するため、迅速に2xxステータスで応答します。処理に時間がかかる場合は、イベントをキューに入れて非同期で処理します。リトライが必要な一時的なエラーにのみ5xxを返し、永続的なエラー(例:無効なペイロード)の場合は無駄なリトライを防ぐため4xxを返します。
比較:プッシュ vs. ポーリング
| 側面 | Webhook(プッシュ) | ポーリング |
|---|---|---|
| レイテンシ | ほぼリアルタイム | 間隔に依存 |
| 効率性 | イベント時のみ送信 | 常時リクエスト |
| 複雑さ | リトライ/セキュリティロジックが必要 | シンプルだが無駄が多い |
Webhook実装のテスト
ngrokなどのツールを使用してローカルサーバーを公開し、Webhook配信をシミュレートします。意図的にリクエストを失敗させて、署名検証とリトライ動作を確認します。
FAQ
署名付きWebhookペイロードとは何ですか?
署名付きペイロードには暗号署名(多くの場合HMAC)が含まれ、受信者は共有秘密鍵を使用してリクエストの真正性と整合性を検証できます。
失敗したWebhookを何回リトライすべきですか?
普遍的な回数はありませんが、指数バックオフで3〜5回のリトライが一般的です。その後は、手動レビューのために失敗をログに記録します。
HTTPSなしでWebhookを使用できますか?
技術的には可能ですが、非常に安全ではありません。盗聴や中間者攻撃を防ぐため、常にHTTPSを使用してください。
Webhookペイロードを検査する準備はできましたか?JSONフォーマッターを使用して、JSONペイロードをすばやく整形して検証しましょう。