Stripe Webhookの署名検証が失敗したら、raw request body、Stripe-Signature header、endpoint固有のsigning secretを分けて確認します。検証を外して処理を通したり、parse済みJSONを再文字列化してraw bodyの代わりにしたりせず、受信から検証関数へ渡る経路を追います。
この記事は署名検証が通らない入口の診断です。検証後のイベント重複や業務処理の再試行とは分けます。Stripeへの接続、イベント送信・転送、決済操作、secretの閲覧・変更、framework上の実測は行っていません。以下は架空の確認表です。
目次
三つの入力を別々に照合する
Stripeのendpoint管理・署名検証資料(新しいタブで開く)と公式Node.js SDK(新しいタブで開く)を起点に、利用SDKが要求する入力形式を確認します。実際の例外は、secretを含まない分類と発生時刻、endpoint識別で記録します。
表は横にスクロールできます
| 入力 | 確認すること | よくある取り違え |
|---|---|---|
| raw body | 検証前にbyte列や文字列が変わっていないか | JSON.parse後のobjectを再serialize |
| header | 受信したStripe-Signatureを渡しているか | body内の似た値や別header |
| signing secret | この配信先・経路のsecretか | API secret key、別endpointのsecret |
secretや署名値、全payloadを一般ログへ出しません。bodyには顧客情報が含まれる場合があります。調査では、取得方法、型、長さ、middlewareの順序、設定の参照元などから先に確認します。必要な受信データを保持する場合は、閲覧者・保存期間・用途を限定します。
CLI転送とDashboard配信のsecretを分ける
Stripe公式資料は、CLIで転送するendpointのsecretと、Dashboardで管理するendpointのsecretは異なると説明しています。同じlocalhostへ届いても、どこから配信されたかが違えば検証に使う設定も確認が必要です。
表は横にスクロールできます
| 架空の経路 | secretを照合する対象 | 確認する対応 |
|---|---|---|
| stripe listenからlocal転送 | そのCLIのlistenで示されたsecret | 現在の転送と受信設定 |
| 登録endpointへの直接配信 | Workbenchの対象endpoint | URL・環境・endpoint識別 |
| 別環境の同名handler | その環境用の設定参照 | dev/test/liveの取り違え |
| rotation後のhandler | 対象endpointの有効secretと設定 | 実行processへの反映 |
値のprefixが似ていることを、同じsecretの証明にはしません。設定を更新しても、稼働processが古い参照を読む場合は反映されていない可能性があります。むやみに再発行する前に、対象endpointと設定の供給経路を固定します。
bodyを変えるmiddlewareの位置を確認する
Stripeは、署名検証へ元のraw bodyを渡す必要を説明しています。空白、改行、key順序などが再serializeで変われば、内容が意味上同じJSONでも元の署名対象と同じとは限りません。JSON.stringifyしたobjectをraw bodyへ戻す修復にはしません。
ExpressなどでJSON parserを全routeへ先に適用すると、Webhook routeへ来た時点で元のbodyを失う構成があります。Webhook routeでraw bodyを取得する方法と、parserの適用順をframeworkの現行手順に合わせて確認します。別frameworkへExpressのmiddlewareをそのまま移植する必要はありません。
表は横にスクロールできます
| 段階 | 調べる箇所 | 正しくても残る未確認 |
|---|---|---|
| proxy・gateway | body変換・encoding処理 | handler以降の加工 |
| framework入口 | parserとrouteの順序 | SDKへ渡す値 |
| body読取 | streamを誰が消費したか | 別middlewareとの競合 |
| 検証呼出 | raw/header/secretの対応 | 検証後の業務処理 |
bodyを一度しか読めないrequest APIでは、前段の処理が消費していないかを確認します。serverless gateway等に独自のbody表現がある場合は、公式の該当integration手順へ照合し、base64や文字列を推測で何度も変換しません。
時刻や再送を調べても検証を緩めない
入力が揃って見えても失敗する場合は、SDKの版、例外の分類、サーバー時刻、timestamp検査の条件を確認します。古い保存payloadと古い署名を手動で再利用した試験は、現在時刻の検査に一致しない場合があります。署名の有効性を調べる試験と、業務処理のfixture試験を分けます。
エラーを消す目的で署名検証を無効化したり、許容時間を無制限に広げたりしません。未検証のbodyを信用して顧客データや注文状態を変更しないことを、入口の失敗条件として保持します。
修正は一項目ずつ検証する
- 対象endpoint・配信経路・環境を固定する。
- その経路のsigning secretを、秘密値を記録せず参照元で照合する。
- bodyを最初に読む箇所から検証関数まで、加工とparserの順序を追う。
- header取得とSDKへ渡す入力形式を照合する。
- 許可された検証環境で正規の新しい配信を受け、検証結果を確認する。
- 改変や不正入力が検証を通らず、業務処理へ進まないことも確認する。
変更したmiddlewareや設定は差分で残し、うまくいかなければその変更だけを戻せるようにします。raw bodyの検証を直す過程で、他routeのJSON処理を壊していないかも点検します。
署名成功と業務完了を分ける
署名が通ったら、event ID・type等の必要な情報を使い、安全な保存やqueueへ受け渡します。HTTP 2xxを返しただけで業務処理の成功とはしません。正当な再送でも同じ副作用を繰り返さない設計は、署名検証とは別に必要です。
復旧記録には、失敗していた入力経路、変更点、正規配信と不正入力の結果、受信後の未確認事項を残します。secretの実値や顧客payloadを添付せず、endpointと設定の対応が追える状態を維持します。
