Stripe Webhookの署名検証が失敗する原因を切り分ける|Raw Body・Endpoint Secret・Header

Stripe Webhook署名の三つの入力を、body加工、header取得、CLIとDashboardのsecretから切り分け、検証前の副作用を防ぎます。

AIものさし編集部

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の対象endpointURL・環境・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・gatewaybody変換・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を信用して顧客データや注文状態を変更しないことを、入口の失敗条件として保持します。

修正は一項目ずつ検証する

  1. 対象endpoint・配信経路・環境を固定する。
  2. その経路のsigning secretを、秘密値を記録せず参照元で照合する。
  3. bodyを最初に読む箇所から検証関数まで、加工とparserの順序を追う。
  4. header取得とSDKへ渡す入力形式を照合する。
  5. 許可された検証環境で正規の新しい配信を受け、検証結果を確認する。
  6. 改変や不正入力が検証を通らず、業務処理へ進まないことも確認する。

変更したmiddlewareや設定は差分で残し、うまくいかなければその変更だけを戻せるようにします。raw bodyの検証を直す過程で、他routeのJSON処理を壊していないかも点検します。

署名成功と業務完了を分ける

署名が通ったら、event ID・type等の必要な情報を使い、安全な保存やqueueへ受け渡します。HTTP 2xxを返しただけで業務処理の成功とはしません。正当な再送でも同じ副作用を繰り返さない設計は、署名検証とは別に必要です。

復旧記録には、失敗していた入力経路、変更点、正規配信と不正入力の結果、受信後の未確認事項を残します。secretの実値や顧客payloadを添付せず、endpointと設定の対応が追える状態を維持します。

確認した公式資料