Webhook署名検証:連携を安全にする方法

Ashley Innocent

Ashley Innocent

22 12月 2025

Webhook署名検証:連携を安全にする方法

Webhookは、サードパーティサービスからリアルタイムの更新を受け取るための最も強力な方法の一つです。Stripe、GitHub、Shopify、Twilioからの単一のHTTP POSTは、顧客への請求、リポジトリの更新、注文の発送、確認SMSの送信など、アプリケーションの重要なビジネスロジックをトリガーできます。

しかし、すべてのWebhookリクエストは公共のインターネット経由で届きます。つまり、あなたのWebhook URLを推測したり発見したりした人は誰でも、完全に正当に見える悪意のあるペイロードを送信できるということです。適切な認証がなければ、あなたのアプリケーションは本物のイベントと偽造されたイベントを区別する方法がありません。

そこで登場するのがWebhookシグネチャ検証です。これは、受信するすべてのWebhookリクエストが、期待するサービスから本当に送信されたものであり、転送中に改ざんされていないことを保証するシンプルで標準化されたメカニズムです。

この包括的なガイドでは、Webhookシグネチャ検証がどのように機能するのか、そして一般的な言語でそれを正しく実装する方法を正確に学びます。また、避けるべき一般的な間違いや、すべてを迅速かつ確実にエンドツーエンドでテストする方法も紹介します。

💡
技術的な詳細に入る前に、Webhookを数分で検証しデバッグするための適切なツールがあることを確認しておきたいです。Apidogは完全に無料でダウンロードでき、組み込みのWebhookシグネチャ検証テスト、モックサーバー、リアルタイムペイロード検査を提供しています — クレジットカードは不要です。これは、あなたの検証ロジックが実際に機能することを最も早く確認する方法です。

ボタン

Webhookシグネチャ検証とは?

Webhookシグネチャ検証とは、受信したWebhookリクエストが実際に期待するサービスから送信されたものであり、改ざんされていないことを確認するプロセスです。

ほとんどのプロバイダーは、SHA-256またはSHA-512を使用したHMAC(Hash-based Message Authentication Code)を使用しています。サービスは以下を計算します。

signature = HMAC-SHA256(secret_key, payload)

その後、シグネチャをヘッダー(通常はX-SignatureSignature、またはX-Hub-Signature-256)で送信します。

あなたのサーバーは次の手順を踏みます。

  1. ペイロードを未加工のバイトとして受信します(重要!)
  2. 保存されているシークレットを使用してHMACを再計算します
  3. 計算されたシグネチャと受信したシグネチャを比較します

それらが完全に一致する場合、Webhookを処理します。そうでなければ、HTTP 401または403を返します。

HMAC-SHA256が業界標準である理由

プロバイダーがHMAC-SHA256を選ぶのには明確な理由があります。

GitHub、Stripe、Shopify、Slack、その他多数のサービスがHMAC-SHA256を使用しています。

Node.jsでWebhookシグネチャ検証を実装する方法

Node.jsでの実例から始めましょう。

const crypto = require('crypto');

function verifyWebhookSignature(payload, signature, secret) {
  const hmac = crypto.createHmac('sha256', secret);
  const computedSignature = hmac.update(payload).digest('hex');

  return crypto.timingSafeEqual(
    Buffer.from(computedSignature),
    Buffer.from(signature)
  );
}

注目すべき重要点:

Expressミドルウェアの例:

app.post('/webhooks/stripe', (req, res, next) => {
  const signature = req.headers['stripe-signature'];
  const secret = process.env.STRIPE_WEBHOOK_SECRET;

  // Get raw body (Express needs middleware to preserve raw body)
  const rawBody = req.rawBody || req.body; // use body-parser with verify option

  if (!verifyWebhookSignature(rawBody, signature, secret)) {
    return res.status(401).send('Invalid signature');
  }

  // Signature is valid → process the event
  next();
});

Python実装 (FastAPI + Pydantic)

from fastapi import FastAPI, Request, HTTPException
import hmac
import hashlib

app = FastAPI()

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

    return hmac.compare_digest(computed, signature)

@app.post("/webhooks/github")
async def github_webhook(request: Request):
    signature = request.headers.get("X-Hub-Signature-256")
    if not signature:
        raise HTTPException(status_code=401, detail="Missing signature")

    payload = await request.body()

    if not verify_signature(payload, signature.split('=')[1], SECRET):
        raise HTTPException(status_code=401, detail="Invalid signature")

    # Process webhook
    return {"status": "ok"}

開発者が陥りやすい一般的な落とし穴(とその回避方法)

1. JSON.stringify()またはパースされたボディを使用する

多くのフレームワークはJSONを自動的にパースします。空白、キーの順序、書式設定が異なるため、これが検証を破綻させます。

解決策:常にパースする前に生のボディをキャプチャしてください。

2. 文字列を === で比較する

タイミング攻撃によって情報が漏洩する可能性があります。crypto.timingSafeEqualまたはhmac.compare_digestを使用してください。

3. コード内にシークレットを保存する

環境変数またはシークレットマネージャー(AWS Secrets Manager、HashiCorp Vaultなど)を使用してください。

4. リプレイ攻撃の処理を忘れる

ほとんどのプロバイダーはタイムスタンプを含んでいます。イベントが最近のものであること(例:5分以内)を確認してください。

const timestamp = req.headers['X-Signature-Timestamp'];
if (Date.now() - timestamp > 5 * 60 * 1000) {
  return res.status(401).send('Timestamp too old');
}

5. SHA-1の使用(まだ発生しています!)

GitHubは2022年にSHA-1を非推奨にしました。常にSHA-256を使用してください。

ApidogでWebhookシグネチャ検証をテストする

Webhookを手動でテストするのは手間がかかります。リクエストを送信し、ログを確認し、修正し、繰り返すことになります。

Apidogはこれを簡単にしてくれます。

Apidogプロジェクトで、左サイドバーの+アイコンをクリックし、「New Other Protocol APls」>「Webhook」を選択します。

CleanShot 2025-11-05 at 17.18.02@2x.png

Webhookを作成したら、エディターで以下のフィールドを埋めます。リクエストメソッド:通常はPOSTWebhook名:APIドキュメントとOpenAPIエクスポートに表示されます(例:order)。デバッグURL(オプション):テストリクエストの送信に使用される実際のURL。注:これはテスト目的のみであり、ドキュメントには含まれませんその他の情報:リクエストボディなど。

image.png

必要なすべてのフィールドに入力したら、保存をクリックしてください。
デバッグURLフィールドにWebhook URLを入力し、送信をクリックするだけで、Webhook呼び出しをシミュレートできます。

image.png

ApidogのWebhookシミュレーターのおかげで、デバッグ時間を何時間も節約できました。Stripeの正確なstripe-signature形式やGitHubのsha256=...プレフィックスにも対応しています。

実例:Stripe Webhookの検証

Stripeは特殊なヘッダー形式を使用しています。

stripe-signature: t=1681234567,v1=abc123...,v0=def456...

次のことを行う必要があります。

Stripeはこの複雑さを処理するための公式ライブラリを提供しています。

const stripe = require('stripe')('sk_...');
stripe.webhooks.constructEvent(payload, sigHeader, endpointSecret);

しかし、自分で実装する必要がある場合は、基盤となるHMACを理解することが重要です。

高度なトピック:複数のシグネチャを許容する

一部のプロバイダー(Stripeなど)は、後方互換性のために複数のシグネチャを送信します。あなたのコードは次のことを行うべきです。

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

結論:小さな検証ステップ、大きなセキュリティ上の利益

Webhookシグネチャ検証は些細な詳細のように聞こえるかもしれません。しかし、これは安全なアプリケーションと、攻撃者が簡単に悪用できるアプリケーションとの違いを生みます。

これを正しく実装し、Apidogのようなツールで徹底的にテストすることで、あなたの統合が保護されていることを知って安らかに眠ることができます。

今すぐApidogを無料でダウンロードして、5分以内に最初のWebhookを検証しましょう。これは、あなたのコードが実際に機能することを証明する最速の方法です。

ボタン

ApidogでAPIデザイン中心のアプローチを取る

APIの開発と利用をよりシンプルなことにする方法を発見できる