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

Backend2026-10-21TryQuickToolBox

ユーザーのエンドポイントにWebhookを送信するAPIを構築したとします。しかし、そこには古典的な問題が待ち受けています。ペイロードが改ざんされていないことをどう保証するのか?エンドポイントがダウンしたらどうなるのか?重複処理をどう防ぐのか?この記事では、署名付きペイロードと堅牢なリトライ戦略で、これらの課題に正面から取り組みます。

Webhookに署名が必要な理由

Webhookは、イベントを他のシステムに通知するHTTPコールバックです。公共のインターネット上を送信されるため、傍受やなりすましの脆弱性があります。署名がなければ、悪意のある攻撃者が偽のWebhookをユーザーに送信し、意図しないアクションを引き起こす可能性があります。共有シークレットでペイロードに署名することで、真正性と完全性が保証されます。

署名付きペイロードの仕組み

最も一般的なアプローチは、SHA-256を使用したHMAC(Hash-based Message Authentication Code)です。送信者と受信者は秘密鍵を共有します。送信者はペイロード(場合によってはタイムスタンプも)に対して署名を計算し、ヘッダーに含めます。受信者は署名を再計算し、受信したものと比較します。

ステップバイステップ:ペイロードへの署名

  1. ペイロードの作成:イベントデータをJSONにシリアライズします。署名は正確なバイト列に対して計算されるため、一貫したフォーマット(余分な空白がないなど)を確保してください。
  2. タイムスタンプの追加:リプレイ攻撃を防ぐため、ペイロードまたはヘッダーにUnixタイムスタンプを含めます。受信者は数分以上古いリクエストを拒否できます。
  3. HMACの計算:秘密鍵を使用して、ペイロード文字列(またはタイムスタンプ + ペイロード)のHMAC-SHA256を生成します。
  4. 署名の添付:X-Signatureのようなヘッダーで送信します。

Pythonでの例を示します:

import hmac
import hashlib
import json
import time

secret = b'your_shared_secret'
payload = {"event": "user.created", "data": {"id": 123}}
body = json.dumps(payload, separators=(',', ':'))
timestamp = str(int(time.time()))
signed_content = timestamp + '.' + body
signature = hmac.new(secret, signed_content.encode(), hashlib.sha256).hexdigest()

# Send headers: X-Signature: signature, X-Timestamp: timestamp

署名の検証

受信側では、以下を行う必要があります:

Node.jsでの検証例:

const crypto = require('crypto');

function verifySignature(req, secret) {
  const signature = req.headers['x-signature'];
  const timestamp = req.headers['x-timestamp'];
  if (!signature || !timestamp) return false;
  if (Date.now() / 1000 - timestamp > 300) return false; // 5 min
  const body = JSON.stringify(req.body);
  const signed = timestamp + '.' + body;
  const expected = crypto.createHmac('sha256', secret).update(signed).digest('hex');
  return crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected));
}

リトライ戦略の設計

署名があっても、ネットワークの問題、サーバーエラー、タイムアウトによりWebhookは失敗する可能性があります。堅牢なリトライ戦略により、受信者に過負荷をかけることなく配信を保証します。

主な考慮事項

リトライの実装

ほとんどのWebhook送信者は、リトライをスケジュールするためにキューシステム(例:Redis、RabbitMQ、またはデータベース)を使用します。以下は簡略化したフローです:

  1. イベントトリガー時に、ペイロードとターゲットURLを含むWebhookジョブをキューに入れます。
  2. ワーカーがジョブを取得し、配信を試みます。
  3. レスポンスステータスが2xxの場合、成功としてマークします。
  4. そうでない場合、最大試行回数まで指数バックオフでリトライをスケジュールします。
  5. 最大試行回数後、失敗としてマークし通知します。

リトライスケジュールの例:

試行遅延
1即時
21分
35分
430分
52時間

Webhookセキュリティのベストプラクティス

FAQ

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

署名付きペイロードには、受信者が送信者の真正性とペイロードが改ざんされていないことを検証できる暗号署名(通常はHMAC)が含まれています。

失敗したWebhookを何回リトライすべきですか?

万能の答えはありませんが、一般的なアプローチは指数バックオフで3〜5回のリトライです。イベントの重要度と受信者の容量を考慮してください。

Webhook署名にJWTを使用できますか?

はい、JWTは使用できますが、より複雑です。HMACはシンプルで、ほとんどのユースケースで十分です。JWTは、署名自体にクレームや有効期限を含める必要がある場合に便利です。

Webhookペイロードをテストする準備はできましたか?送信前にJSONペイロードを検証して整形するには、JSON Formatterをご利用ください。