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

Backend2026-10-26TryQuickToolBox

Webhookに署名付きペイロードとリトライが必要な理由

サードパーティサービスを統合し、エンドポイントにWebhookが送信されるようにしたばかりだとします。テストではすべてが機能しますが、本番環境ではイベントの欠落、重複配信、さらに悪いことに、誰かがWebhook URLへのリクエストを偽装していることに気づきます。適切なセキュリティと信頼性の対策がなければ、Webhookはバグや脆弱性の原因になり得ます。

Webhookは、イベントが発生したときにアプリケーションに通知するHTTPコールバックです。概念はシンプルですが、信頼境界とネットワークの信頼性の問題を横断するため、実際には注意が必要です。このガイドでは、2つの重要な側面を扱います:署名付きペイロードによる真正性の検証と、リトライによる配信の保証です。すぐに適用できる実践的な実装を見ていきましょう。

署名付きペイロードの理解

署名付きペイロードは、送信者がWebhookが自分から送信され、改ざんされていないことを証明する方法です。送信者は共有シークレットを使用してリクエストボディ(場合によってはヘッダー)の暗号署名を計算し、その署名をヘッダーに含めます。エンドポイントは署名を再計算して比較します。

最も一般的なアルゴリズムはHMAC-SHA256です。高速で安全、そして広くサポートされています。仕組みは次のとおりです:

  1. 送信者と受信者は秘密鍵を共有します(Webhook設定で構成)。
  2. 各Webhookについて、送信者はHMAC-SHA256を使用してペイロードを秘密鍵でハッシュ化し、署名を作成します。
  3. 署名はX-Signatureのようなヘッダーで送信されます。
  4. 受信者は同じHMACを計算し、タイミング攻撃を防ぐために定数時間比較を使用して比較します。

Node.jsでのHMAC検証の実装

具体的な例を見てみましょう。送信者がX-Signatureに16進文字列として署名を含めるとします。Expressルートで検証する方法は次のとおりです:

const crypto = require('crypto');
const express = require('express');
const app = express();

// 署名検証には生のボディを使用
app.use('/webhook', express.raw({ type: 'application/json' }));

app.post('/webhook', (req, res) => {
  const signature = req.headers['x-signature'];
  const secret = process.env.WEBHOOK_SECRET;

  const expected = crypto
    .createHmac('sha256', secret)
    .update(req.body)
    .digest('hex');

  if (!crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected))) {
    return res.status(401).send('Invalid signature');
  }

  // イベントを処理
  const event = JSON.parse(req.body);
  console.log('Received event:', event.type);

  res.status(200).send('OK');
});

app.listen(3000);

重要:署名の計算には常に生のリクエストボディを使用し、解析済みのJSONは使用しないでください。解析と再文字列化により空白や順序が変わり、署名が壊れる可能性があります。

タイムスタンプとリプレイ保護

署名だけではリプレイ攻撃を防げません。攻撃者は有効な署名付きリクエストをキャプチャし、後で再送信する可能性があります。これを軽減するには、署名付きペイロードまたはヘッダーにタイムスタンプを含めます。たとえば、Unix時間を含むX-Timestampヘッダーを追加します。受信者はタイムスタンプが許容範囲(例:5分)内であることを確認し、古いリクエストを拒否します。

また、改ざんを防ぐためにタイムスタンプを署名計算に含めます。送信者はtimestamp + '.' + bodyに対してHMACを計算します。受信者も同様に計算して比較します。

信頼性の高い配信のためのリトライ実装

Webhookはパブリックインターネットを経由するため、障害は避けられません:ネットワークタイムアウト、サーバーエラー(5xx)、またはエンドポイントが一時的にダウンしている場合などです。堅牢なWebhookシステムは失敗した配信をリトライする必要があります。

リトライ戦略

一般的なリトライ戦略には次のものがあります:

典型的なポリシーは、10秒から始めて指数バックオフとジッターで最大5回リトライするというものです。その後、配信を失敗としてマークし、場合によってはアラートを出します。

冪等性:重複の処理

リトライは重複配信を引き起こす可能性があります。エンドポイントは冪等である必要があります。つまり、同じイベントを2回処理しても、顧客への二重請求などの副作用を引き起こしてはなりません。ペイロードに一意のイベントID(例:event.id)を含め、処理済みIDを妥当な期間保存します。処理前に、IDが既に確認されているかチェックします。

あるいは、操作を自然に冪等になるように設計します(例:挿入ではなくupsertを使用)。

迅速な応答

Webhookエンドポイントは迅速に受信を確認し(数秒以内)、イベントを非同期で処理する必要があります。処理に時間がかかりすぎると、送信者がタイムアウトしてリトライし、重複が発生する可能性があります。キューを使用します:署名を検証した直後に200 OKで応答し、その後イベントをバックグラウンド処理用にエンキューします。

Webhookセキュリティ対策の比較

対策 目的 実装の複雑さ
HMAC署名 真正性と整合性の検証 低
タイムスタンプ検証 リプレイ攻撃の防止 低
冪等性キー 重複処理の回避 中
IP許可リスト 既知の送信者IPに制限 低(ただし脆弱)
HTTPS 転送中のデータを暗号化 低

IP許可リストは層を追加できますが、送信者IPが変更される可能性があるため信頼性はありません。主に署名に依存してください。

Webhookエンドポイントのテスト

ngrokなどのツールを使用してローカルサーバーを公開し、プロバイダーのダッシュボードからテストWebhookをトリガーします。署名が正しく検証されること、および失敗をシミュレートして(例:最初の数回の試行で500を返す)リトライが機能することを確認します。

デバッグ時には、JSONペイロードを検査する必要があるかもしれません。JSON Formatterは、Webhookペイロードを迅速に整形して検証するのに役立ちます。

FAQ

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

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

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

普遍的な数はありませんが、指数バックオフで3〜5回のリトライが一般的です。その後、失敗をログに記録し、アラートを検討します。目標は、無限のリトライなしで一時的な問題を処理することです。

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

はい、一部のプロバイダーはJWT(JSON Web Tokens)を使用してWebhookに署名します。トークンにはクレームが含まれ、秘密鍵または秘密鍵で署名されます。検証は同様です:署名を検証し、有効期限などのクレームを確認します。

安全なWebhookを実装する準備はできましたか?強力なシークレットを生成し、HMAC検証を追加し、冪等性を備えたリトライを設定することから始めましょう。開発中の迅速なペイロード検査には、JSON Formatterを試してWebhook JSONを整形・検証してください。