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

Backend2026-10-14TryQuickToolBox

Webhookが厄介な理由

Webhookはシンプルなアイデアです。APIをポーリングして変更を確認する代わりに、プロバイダーがHTTP POSTでイベントをエンドポイントにプッシュします。しかし実際には、Webhookは多くの開発者を悩ませる一連の課題をもたらします。リクエストが本当にプロバイダーから来たのかを検証すること、重複配信の処理、ネットワーク障害への対処などです。これらを無視すると、セキュリティ侵害、データ破損、ユーザーの怒りを招くリスクがあります。

この記事では、本番環境レベルのWebhookの最も重要な2つの側面、署名付きペイロードとリトライについて説明します。署名の検証方法、指数バックオフを用いたリトライの実装方法、そして処理を冪等にする方法をカバーします。

署名付きペイロード:真正性の証明

誰でもあなたのエンドポイントにHTTP POSTを送信できます。リクエストがプロバイダーから来たことを確認するために、プロバイダーは秘密鍵でペイロードに署名し、その署名をヘッダーで送信します。あなたの仕事は、署名を再計算して比較することです。

ほとんどのプロバイダーはHMAC-SHA256を使用します。署名は通常、生のリクエストボディ(場合によってはタイムスタンプ付き)に対して計算され、16進数でエンコードされます。例えば、Stripeはタイムスタンプと署名を含むStripe-Signatureのようなヘッダーを使用します。

HMACの仕組み

HMAC(Hash-based Message Authentication Code)は、秘密鍵とハッシュ関数を組み合わせます。同じ鍵とメッセージが与えられれば、出力は決定論的です。鍵がなければ、攻撃者は有効な署名を偽造できません。

以下はNode.jsでの最小限の例です:

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)
  );
}

Pythonでも同様に簡単です:

import hmac
import hashlib

def verify_signature(payload: bytes, signature: str, secret: str) -> bool:
    expected = hmac.new(
        secret.encode(),
        payload,
        hashlib.sha256
    ).hexdigest()
    return hmac.compare_digest(expected, signature)

署名検証のベストプラクティス

タイムスタンプとリプレイ攻撃への対処

多くのプロバイダーは署名ヘッダーにタイムスタンプを含めます。例えば、ヘッダーはt=1614556800,v1=abc123...のようになります。次のようにします:

  1. タイムスタンプと署名を抽出します。
  2. タイムスタンプが許容範囲内(例:5分)であることを確認します。
  3. タイムスタンプとペイロードを使用して署名を再計算します。
  4. 署名を安全に比較します。

これにより、攻撃者が古い有効なリクエストをリプレイするのを防ぎます。

リトライ:障害を優雅に処理する

Webhookはパブリックインターネット経由で配信されるため、障害は避けられません。エンドポイントがダウンしていたり、遅かったり、500エラーを返したりする可能性があります。堅牢なプロバイダーは失敗した配信をリトライしますが、あなたがプロバイダーである場合は、あなたの側でもリトライを処理する必要があります。

ジッター付き指数バックオフ

リトライする際は、即時のリトライでエンドポイントを叩き続けるのを避けます。指数バックオフを使用します:各失敗後に待機時間を長くします。同時に多くのWebhookが失敗したときのサンダリングハード問題を避けるために、ジッター(ランダム性)を追加します。

典型的なリトライスケジュールは次のようになります:

試行回数遅延(約)
1即時
230秒
35分
430分
52時間

最大試行回数に達したら、配信を失敗としてマークし、アラートを出します。

冪等性:安全なリトライの鍵

リトライは重複配信を引き起こす可能性があるため、Webhookハンドラーは冪等でなければなりません。つまり、同じイベントを複数回処理しても、1回処理した場合と同じ効果が得られる必要があります。

冪等性を実現する方法:

プロバイダー側でのリトライ実装

Webhookシステムを構築する場合、信頼性の高い配信メカニズムが必要です。以下は簡略化されたアプローチです:

  1. イベントが発生したら、ペイロードとターゲットURLを含む配信ジョブをキューに入れます。
  2. ワーカーがジョブを取得し、タイムアウト(例:10秒)付きでHTTP POSTを送信します。
  3. レスポンスステータスが2xxでない場合、指数バックオフでリトライをスケジュールします。
  4. 試行回数を追跡し、最大回数(例:5回)後に諦めます。
  5. 失敗をログに記録し、ユーザーが手動でリプレイできる方法を提供します。

永続キュー(例:Redis、RabbitMQ、またはデータベーステーブル)を使用して、再起動後もリトライが生き残るようにします。

まとめ:Webhookレシーバーのチェックリスト

Webhookレシーバーを構築する際は、次のチェックリストに従ってください:

FAQ

署名付きWebhookペイロードとは何ですか?

署名付きペイロードには、共有秘密鍵で生成された暗号署名(通常はHMAC-SHA256)が含まれます。受信者は署名を再計算して、リクエストが期待される送信者から来たものであり、改ざんされていないことを検証します。

Webhookにリトライが必要なのはなぜですか?

ネットワークの問題、サーバーのダウンタイム、一時的なエラーにより、Webhook配信が失敗する可能性があります。指数バックオフ付きのリトライにより、受信者に過負荷をかけずにイベントが最終的に配信されることが保証されます。

Webhookの重複処理を防ぐにはどうすればよいですか?

冪等性を使用します:ペイロードに一意のイベントIDを含め、処理済みIDを保存し、操作を冪等に設計します(例:アップサート)。これにより、Webhookが複数回配信されても効果は同じになります。

Webhookペイロードを検査またはデバッグする必要がある場合、JSON FormatterがJSONデータを迅速に整形して検証するのに役立ちます。