Skip to main content
サーバーに送信するすべてのリクエスト(webhook 配信および ツールエンドポイント呼び出し)には、 X-ThunderPhone-Signature ヘッダーに HMAC-SHA256 署名が含まれます。一度正しく 検証を実装し、同じヘルパーをすべてのハンドラーに組み込んでください。

アルゴリズム

  1. 生のリクエスト本文(POST した正確なバイト列)を読み取ります。
  2. hmac_sha256(secret, body).hexdigest() を計算します。
  3. X-ThunderPhone-Signature定数時間で比較します。 (単純な文字列比較ではタイミング情報が漏洩します。)
送信するバイト列そのものに署名するため、生の本文を検証すれば 常に機能します。これらのバイト列は、ペイロードの正規 JSON シリアライズ でもあります。キーはアルファベット順にソートされ、区切り文字はコンパクト (空白なしの ,:)、UTF-8 です。フレームワークがパース済み JSON しか公開しない場合は、 完全に同等の別の方法として、正規形式で再シリアライズして HMAC を計算できます。
生の本文を推奨します。手順が 1 つ少なく、一部の言語における JSON 数値の ラウンドトリップの問題の影響を受けません。

使用するシークレット

シークレットはシークレットマネージャーまたは環境変数に保存し、コミットしないでください。

リファレンス実装

以下の 4 つはいずれも生のリクエスト本文を検証します。

フレームワーク別の実装

ツール呼び出しの検証

エージェントが 関数ツールを直接呼び出す場合(ツールに endpoint がある場合)、設定した endpoint.headers に加えて、 リクエストには次の 2 つの ThunderPhone ヘッダーが含まれます。
  • X-ThunderPhone-Call-ID — 進行中の通話の数値 ID。
  • X-ThunderPhone-Signature — リクエスト本文の完全一致するバイト列に対して 組織レベルのWebhookシークレットをキーに使用した HMAC-SHA256。
同じ verify() ヘルパーを変更せずに使用できますが、次の 2 点に注意してください。
  1. GET / DELETE ツールには本文がありません。 引数はクエリ パラメーターとして渡され、署名は空のバイト文字列に対して計算されます。 したがって、Python では verify(b"", sig, secret)、Node では verify(Buffer.alloc(0), sig, secret) を使用します。クエリ文字列をハッシュ化 しないでください。
  2. レガシーWebhookが設定されていない組織には、組織シークレットがありません。 この場合、ツール呼び出しには X-ThunderPhone-Call-ID のみが含まれ、署名 ヘッダーは含まれません。署名用シークレットを取得するにはレガシーWebhook (PUT /v1/webhook)を設定するか、endpoint.headers を通じて独自の ヘッダーでツール呼び出しを認証してください。
Webhookモードのツールディスパッチ(endpoint のないツールが telephony.tool / web.tool として組織Webhookに配信される場合)は、通常の 署名付きWebhookです。上記の標準手順を適用してください。両方のリクエスト形式については 関数ツールを参照してください。

よくある落とし穴

ボディを解析し、JSON ライブラリのデフォルト設定 (, / : の後のスペース、挿入順のキー)で再出力すると、 バイト列が変わり、HMAC が破損します。生のボディを検証してください。再シリアライズが必要な場合は、 キーのソート、コンパクトな区切り文字、UTF-8 という正規形式に完全に一致させてください。
Express の express.json() ミドルウェアはボディストリームを消費するため、 生のバイト列が失われます。Webhook ルートには特に express.raw() を使用するか、 前処理ミドルウェアで生のボディをバッファリングしてください。 NestJS / Koa でも同様です。「raw body」のドキュメントを確認してください。
JS の expected === signature や Python の expected == signature は 実行時間が変動する比較です。それぞれ crypto.timingSafeEqual または hmac.compare_digest を使用してください。パフォーマンス上の差は ありません。
ツールエンドポイントへの直接呼び出しは、組織レベルの Webhook シークレットGET /v1/webhook)で署名されます。/v1/developer/webhook-endpoints のエンドポイントごとのシークレットは使用しません。同じ verify() 関数を再利用できますが、ツールルートでは組織のシークレットを渡してください。
ボディのないツールメソッドでは、署名の対象は空のバイト文字列です。これにより、リクエストボディが何であっても 生のリクエストボディを HMAC にかけるという、共通の手順を維持できます。 URL やクエリ文字列をハッシュ化しても一致することはありません。
検証失敗時に 200 を返すと、ハンドラーがリプレイ攻撃の 標的になります。検証に失敗した場合は、必ず 2xx 以外を返してください。

次のステップ

Webhook の概要

配信セマンティクス、再試行、送信元 IP。

Webhook エンドポイント

複数の URL を管理し、シークレットをローテーションします。

Function Tools

2 つのツール呼び出しパスと、それぞれのリクエスト形式。

ツール連携

ツールを活用する完全な連携をエンドツーエンドで構築します。