Webhook署名検証のやり方と受信テスト(LINE・Stripe・GitHub・Slack ほか)
Webhook は、サービスの側から自分のサーバーへ HTTP リクエストを送ってくる仕組みです。作っている途中は「そもそも届いているのか」「中身はどんな形か」「署名の検証がなぜ通らないのか」が分かりにくくなりがちです。このページでは、送信元ごとに確かめる点をまとめます。Webhook が初めての人は、先にはじめてのフック箱を読むと分かりやすいです。
まず、届いているかを確かめる
- フック箱で受信用 URL を発行し、送信元の Webhook の送り先に貼ります。
- 送信元の管理画面から、テスト送信や実際の操作を行います。
- 届けば一覧に出ます。出なければ、送信元の設定(URL の打ち間違い、Webhook が有効になっているか、送るイベントの種類)を見直します。
中身の形が分かったら、フック箱が作る curl コマンドで、自分の開発中のサーバー(localhost など)へ同じリクエストを送り直して動作を確かめられます。
署名の検証が通らないときに多い原因
- 本文を読み直してから署名を計算している。署名は、送られてきたバイト列そのものに対して計算されています。フレームワークが JSON として読み込んでから文字列に戻すと、空白や項目の順番が変わって一致しません。受け取ったままの本文(raw body)で計算します。
- 違う鍵を使っている。本番とテスト、ダッシュボードで作ったエンドポイントと CLI の転送で、鍵がそれぞれ違うことがあります。
- 形式を取り違えている。Base64 と16進(hex)、前に付く文字(
sha256=・v0=・v1,)を確かめます。 - 時刻のずれ。タイムスタンプ付きの署名は、古すぎるものを拒否するのが普通です(多くは5分)。
送信元ごとの署名の仕組み
| 送信元 | 署名ヘッダー | 署名する文字列 | 形式 | 鍵 |
|---|---|---|---|---|
| LINE | x-line-signature | 本文 | HMAC-SHA256・Base64 | チャネルシークレット |
| Stripe | Stripe-Signature(t=…,v1=…) | {t}.{本文} | HMAC-SHA256・16進 | 署名シークレット(whsec_…) |
| Shopify | X-Shopify-Hmac-SHA256 | 本文 | HMAC-SHA256・Base64 | アプリのクライアントシークレット |
| GitHub | X-Hub-Signature-256(sha256=…) | 本文 | HMAC-SHA256・16進 | Webhook の Secret |
| Slack | X-Slack-Signature(v0=…) | v0:{X-Slack-Request-Timestamp}:{本文} | HMAC-SHA256・16進 | Signing Secret |
| makeshop | x-makeshop-signature | {x-makeshop-request-timestamp}:{本文} | HMAC-SHA256・Base64 | シークレットキー |
| Standard Webhooks | webhook-signature(v1,…) | {webhook-id}.{webhook-timestamp}.{本文} | HMAC-SHA256・Base64 | whsec_ の後ろを Base64 デコードしたもの |
| kintone | なし | — | — | — |
フック箱では、受け取ったリクエストを選んで鍵を入れると、上の方法でブラウザの中だけで検証します。鍵はサーバーへ送りません。
LINE(Messaging API)
LINE Developers コンソールの Messaging API 設定にある Webhook URL に、受信用 URL を貼ります。「検証」ボタンを押すと、events が空の配列のリクエストが届きます。これに 200 を返せば検証は成功です。
メッセージのイベントには replyToken が入っています。返信に使えるのは一度だけで、受け取ってすぐに使う必要があります。再送されたイベントは deliveryContext.isRedelivery が true になるので、webhookEventId で二重に処理しないようにします。
Stripe
ダッシュボードの Webhook でエンドポイントを追加し、受信用 URL を貼ります。Stripe-Signature の t はタイムスタンプで、既定では5分より古いものを拒否します。同じイベントが2回以上届くことがあるので、イベント ID で重複を除いてから処理します。テスト環境のイベントは livemode が false です。
Shopify
通知の種類は X-Shopify-Topic(例:orders/create)、店は X-Shopify-Shop-Domain で分かります。同じ通知が2回以上届くことがあるので、X-Shopify-Webhook-Id で重複を除きます。
GitHub
リポジトリの Settings › Webhooks で Payload URL に受信用 URL を貼ります。登録した直後に ping イベントが届きます。Secret を入れていないと X-Hub-Signature-256 は付きません。イベントの種類は X-GitHub-Event、配信ごとの ID は X-GitHub-Delivery です。
Slack
Events API の Request URL を登録すると、type が url_verification のリクエストが届きます。本文の challenge の値をそのまま返すと登録が通ります。Slack には3秒以内に応答を返す必要があるので、時間のかかる処理は後回しにします。フック箱では「返す応答」の設定で、固定の本文を返せます。
makeshop
署名はタイムスタンプと本文を : でつないだ文字列から作られます。公式では、タイムスタンプが5分より古いものを拒否するよう勧めています。
Standard Webhooks(Svix・Resend など)
webhook-id・webhook-timestamp・webhook-signature の3つのヘッダーが付く形式です。鍵は whsec_ の後ろを Base64 デコードしたバイト列です。同じ webhook-id のものは同じ通知の再送です。
kintone
kintone の Webhook には署名がありません。本文の type(ADD_RECORD・UPDATE_RECORD など)と app で何が起きたかが分かります。受け取る URL を知られないようにし、必要なら送信元の IP アドレスで絞ります。
フック箱を使う
フック箱は登録なし・無料で使えます。受信箱は7日で自動的に消え、保存するのは最新の100件までです。