Webhook解説:署名付きペイロードとリトライ

Backend2026-11-09TryQuickToolBox

決済ゲートウェイやCI/CDサービスを統合し、リアルタイムイベントを受信する必要が出てきました。Webhookは標準的なソリューションですが、2つの重要な課題があります。リクエストが本当に送信者から来たものであることを検証すること、そしてサーバーが一時的に利用できない場合でもイベントが失われないようにすることです。この記事では、署名付きペイロードとリトライメカニズムを実装して、堅牢なWebhookレシーバーを構築する方法を説明します。

Webhookとは何か?

WebhookはHTTPコールバックです。アプリケーションが更新のためにAPIをポーリングする代わりに、イベントが発生するたびにサービスが指定したURLにHTTP POSTリクエストを送信します。このプッシュモデルはレイテンシとサーバー負荷を軽減します。ただし、リクエストは外部サービスから発信されるため、信頼できない入力として扱う必要があります。

なぜペイロードに署名するのか?

検証がない場合、Webhookエンドポイントを発見した誰でも偽のイベントを送信でき、不正なアクションを引き起こす可能性があります。共有シークレットでペイロードに署名することで、送信者の身元を確認し、ペイロードが改ざんされていないことを保証できます。最も一般的な方法は、SHA-256を使用したHMAC(Hash-based Message Authentication Code)です。

HMAC署名の仕組み

送信者は、秘密鍵を使用して生のリクエストボディのHMACダイジェストを計算し、それをヘッダー(例:X-Signature)に含めます。サーバーは同じ秘密鍵でHMACを再計算し、受信した署名と比較します。一致すれば、リクエストは本物です。

// Node.jsの例:Webhook署名の検証
const crypto = require('crypto');

function verifySignature(payload, signature, secret) {
  const hmac = crypto.createHmac('sha256', secret);
  hmac.update(payload);
  const digest = hmac.digest('hex');
  // タイミング攻撃を防ぐためにtimingSafeEqualを使用
  return crypto.timingSafeEqual(
    Buffer.from(digest, 'hex'),
    Buffer.from(signature, 'hex')
  );
}

重要:タイミングの違いを通じて情報が漏洩するのを避けるため、常に定数時間比較関数(timingSafeEqualなど)を使用してください。また、HMACは解析されたJSONではなく生のボディに対して計算してください。再シリアライズするとバイトシーケンスが変わる可能性があるためです。

リトライの実装

署名があっても、ネットワークの問題やサーバーエラーによりWebhook配信が失敗することがあります。信頼性の高い送信者は、通常指数バックオフを使用して失敗した配信をリトライします。受信者として、エンドポイントを冪等に設計し、適切なステータスコードを返す必要があります。

送信者側のリトライ戦略

冪等なレシーバーの設計

リトライにより重複配信が発生する可能性があるため、レシーバーは同じイベントを複数回処理しても悪影響がないようにする必要があります。ペイロード(またはヘッダー)に一意のイベントIDを含め、処理済みIDを保存します。イベントIDが既に処理されている場合は、再処理せずにすぐに200 OKを返します。

// 冪等なWebhook処理の擬似コード
function handleWebhook(event) {
  if (processedEvents.has(event.id)) {
    return { status: 200, body: 'Already processed' };
  }
  // イベントを処理...
  processedEvents.add(event.id);
  return { status: 200, body: 'Success' };
}

Webhookのセキュリティと信頼性のベストプラクティス

比較:ポーリング vs. Webhook

側面ポーリングWebhook
レイテンシ間隔に依存ほぼリアルタイム
サーバー負荷高い(頻繁なリクエスト)低い(イベント時のみ)
複雑さ実装が簡単署名検証とリトライ処理が必要
信頼性リトライを自分で制御送信者のリトライポリシーに依存

FAQ

Webhook署名を検証するには?

共有シークレットを使用して生のリクエストボディのHMACを計算し、定数時間比較関数を使用してヘッダーの署名と比較します。

Webhookエンドポイントは何を返すべきですか?

イベントを受信したらすぐに2xxステータスコード(例:200 OK)を返します。4xxまたは5xxを返すと、送信者がリトライしたり、配信を失敗としてマークしたりする可能性があります。

署名なしでWebhookを使用できますか?

推奨されません。署名がないと送信者を検証できず、エンドポイントが偽のリクエストに対して脆弱になります。本番環境では常に署名を使用してください。

Webhookペイロードをデバッグする際、JSONのフォーマットは不可欠です。受信したJSONデータをすばやく整形して検証するには、JSONフォーマッターをご利用ください。