Webhook署名シークレットのローテーション手順|通知を止めずに旧鍵を廃止する方法
Webhook署名シークレットを交換するときは、送信側の設定だけを先に変えず、受信側が新旧両方の署名を検証できる状態を作ってから切り替えます。安全な順番は、対象の棚卸し、新しいsecretの保存、受信側の先行更新、送信側の切替、実配送テスト、監視、旧secretの失効です。
併用期間は一律の日数では決めません。送信サービスが複数secretを同時に有効化できるか、再送がどこまで遡るか、全受信インスタンスへ設定が反映されたかを基準にします。漏えいが疑われる緊急交換では長い併用を避け、旧secretで検証されたイベントを疑わしい配送として分離してください。
Webhookは、決済、注文、アカウント作成、CRM更新、通知、AIジョブ完了などを外部から起動します。署名検証を外すと、第三者が正規イベントに見せかけたリクエストを送れるため、交換作業中でも検証を一時停止してはいけません。Webhookの基本構成から確認したい場合は、Webhookを使った非同期処理基盤も参照してください。
本記事のポイント
- 受信側を先に新旧secret対応へ更新し、送信側切替後の実配送と再送を確認してから旧secretを失効します。
- 併用期間は固定日数ではなく、サービスの複数secret対応、再送仕様、全instanceの反映、旧secret依存件数で決めます。
- 漏えい疑いでは旧secretへ戻さず、未検証イベントを隔離し、新secretへの緊急交換と影響調査を同時に進めます。
ローテーションを始める条件と方式を先に確認する
署名シークレットを交換する主な契機は、定期交換、担当者や委託先の変更、保管場所の移行、誤送信・ログ出力・リポジトリ混入などの漏えい疑い、利用終了です。定期交換は手順の実効性を保つために役立ちますが、カレンダーだけで回すと、使われていないsecretや所有者不明の受信口を見落とします。交換頻度と同時に、誰が作成し、どの受信口が使い、いつ失効できるかを台帳化します。
| 交換の契機 | 優先する目的 | 旧secretの扱い | 完了条件 |
|---|---|---|---|
| 計画的な定期交換 | 手順確認、長期利用の解消、所有者棚卸し | 必要最小限の併用期間を設けられる | 新secretの正規配送と再送を確認し、旧secret依存がゼロ |
| 担当者・委託先・運用環境の変更 | 不要な閲覧権限と共有経路の解消 | 新体制へ切り替え後、旧保管先と権限を停止 | 新しい責任者、保管先、監査ログ、緊急連絡先が確定 |
| 漏えいまたは漏えい疑い | 悪用可能時間の短縮と影響封じ込め | 併用を最小化し、旧secret一致イベントを分離 | 旧secret失効、疑わしい配送調査、影響先への対応が完了 |
| 連携・環境の廃止 | 不要な入口と認証情報の撤去 | 停止後に再利用せず削除 | 送信停止、受信口停止、secret削除、台帳更新が完了 |
次に、送信サービスの仕様を確認します。StripeはWebhook endpointのsecretをローテーションするとき、旧secretを即時失効するか、最長24時間の遅延失効を選べます。遅延中は複数secretが有効で、secretごとに署名が生成されます。一方、GitHubはWebhook設定のsecretを更新でき、受信側はraw payloadをHMAC-SHA256で計算してX-Hub-Signature-256と定数時間比較する方式です。サービスごとに「送信側が新旧を併用する」のか、「受信側だけが新旧を受け入れる」のかが異なるため、同じ切替手順を一律に当てはめません。
再送条件も併用期間に影響します。GitHubは失敗配送を自動再送せず、過去3日間の配送を画面またはAPIから手動再送できます。Stripeは失敗時の再試行ごとに新しいtimestampと署名を生成します。したがって「24時間たったから旧secretを止める」のではなく、各サービスの署名生成、再試行、手動再送、secret失効の仕様を確認し、自社の確認時間を加えて決めます。
変更前に台帳と判断表を作る
ローテーションで最も多い事故は、更新対象の漏れです。同じWebhook URLでも、本番と検証、リージョン、複数テナント、旧システム、バッチ受信口でsecretが分かれていることがあります。送信サービスの管理画面、Secret Manager、アプリ設定、デプロイ基盤、Webhook受信ログを突き合わせ、endpoint単位で次の項目を固定します。
| 台帳項目 | 記録する内容 | 確認理由 |
|---|---|---|
| 受信口 | 送信サービス、endpoint URL、環境、リージョン、テナント、購読イベント | 別環境や旧URLの更新漏れを防ぐ |
| 責任者 | 送信側管理者、受信アプリ責任者、当日作業者、承認者、緊急連絡先 | 設定変更と影響判断を一人に集中させない |
| secret識別 | Secret Managerの論理名、version、作成日、使用環境、閲覧権限 | 値をコピーせず新旧を識別する |
| 検証仕様 | 署名header、アルゴリズム、raw body要件、timestamp許容、定数時間比較 | 交換時に検証方式を壊さない |
| 配送仕様 | 再試行、手動再送、重複、順序保証、2xx応答期限、配送ログ保持 | 取りこぼしと重複処理の確認範囲を決める |
| 切替条件 | 併用可否、開始時刻、監視時間、新secret一致件数、旧secret依存件数 | 感覚ではなく証拠で旧secretを止める |
| 復旧条件 | 停止閾値、影響する業務、再送方法、手動補正、漏えい時に戻せない旧secret | 通常障害とセキュリティ事故を分ける |
secretの値は台帳、チケット、チャット、ソースコード、通常ログへ書きません。Secret Managerなどの専用保管先で新しいversionを作り、アプリは論理名やversion参照で取得します。OWASPは、secretの生成、ローテーション、失効、期限を一つのライフサイクルとして扱い、手作業を減らして自動化することを推奨しています。アクセスできる主体は最小化し、誰がいつ読み書きしたかを監査できる状態にします。
新旧secretを併用する場合、アプリ設定にSECRET_OLDとSECRET_NEWを人手で並べるだけでは不十分です。どちらが現在値で、どちらが移行中か、いつ削除するかが分からなくなるため、version ID、状態、開始時刻、失効予定時刻をメタデータとして持たせます。secret値そのものは出さず、「どのversionで検証に成功したか」だけを安全な識別子で計測します。
新旧secretの併用から旧鍵廃止までを7段階で進める
安全な切替は、送信側と受信側を同時に変える一発作業ではありません。受信側を先に広げ、実配送を確認し、最後に旧secretを狭める段階変更です。各段階に担当者、開始時刻、成功条件、停止条件を付けます。
- 対象と変更枠を固定する:endpoint、環境、購読イベント、責任者、業務影響、再送手段を台帳で確定します。決済や受注など重要イベントでは、低トラフィック帯だけでなく、障害を検知できる担当者が待機できる時間を選びます。
- 新しいsecretを安全に生成・保存する:送信サービスが生成するsecretを受け取り、または十分なエントロピーを持つ値を生成し、Secret Managerの新versionへ保存します。値をチケットへ貼らず、アプリの実行主体だけに読み取り権限を付けます。
- 受信側を先に新旧対応へ更新する:raw bodyと公式headerを使う署名検証を維持し、新secretと旧secretの両方を候補として検証できるようにします。どちらか一つが一致したら正規配送候補として扱い、一致しなければ処理本体へ進めません。比較処理は公式ライブラリまたは定数時間比較を使い、secretや署名計算途中の値をログへ出しません。
- 送信側を新secretへ切り替える:受信インスタンスへの設定反映とhealth checkを確認してから、送信サービス側でsecretを更新します。Stripeのように遅延失効を選べる場合は、必要な確認時間だけ併用します。漏えい疑いでは、便利さのために長い併用期間を選びません。
- テスト配送と実配送を確認する:送信サービスのテスト機能だけでなく、本番の低リスクな正規イベントを1件流し、HTTP 2xx、署名検証、イベント保存、キュー投入、業務処理、監査ログまで通します。同じevent IDを再送し、冪等性によって副作用が重複しないことも確認します。
- 新旧version別に監視する:新secret一致件数、旧secret一致件数、署名不一致率、配送遅延、非2xx、キュー滞留、業務レコード反映件数を追います。旧シークレットを止める合図は、新しい署名が通ったことではなく、旧鍵でしか通らない正規配送がゼロになり、再送も確認できたことです。
- 旧secretを失効し、残骸を消す:送信側で旧secretを失効し、受信側の候補から外し、Secret Managerの旧versionと不要な閲覧権限を無効化します。設定ファイル、バックアップ、CI変数、委託先保管、手順書に値や古い参照が残っていないかを確認し、完了時刻と証跡だけを台帳へ残します。
受信側の更新では、署名検証の前にJSONへparseしたり、proxyがbodyを書き換えたりしないことが重要です。Stripeはraw bodyが変更されると検証に失敗すると説明しており、GitHubもpayloadとheaderを変更せず検証するよう案内しています。secret交換のタイミングでframeworkやmiddlewareまで変更すると、失敗原因を切り分けにくくなるため、検証実装の改修とsecret交換は別の変更として扱います。
Webhook受信口では、署名確認とイベント保存、キュー投入までを短く行い、重い業務処理は非同期へ渡します。contact formのように入口から通知・CRM反映までを追う考え方は、フォーム送信の到達監視にも共通します。署名成功だけで完了にせず、最終的な業務結果まで照合してください。
取りこぼしと検証失敗を監視し、ロールバックを判断する
ローテーション後に見るべきなのはHTTP 200だけではありません。署名検証に失敗したリクエストは処理されないため、表面上はアプリが正常でも、注文やCRM更新が欠落している可能性があります。送信側の配送ログ、受信側の検証ログ、イベント台帳、業務システムの結果をevent IDで照合します。
| 監視項目 | 異常の見え方 | 最初の確認 | 対応 |
|---|---|---|---|
| 署名不一致率 | 切替時刻から403・400が増える | raw body、header名、secret version、全instanceの反映 | 処理を止め、受信設定を修正。検証自体は無効化しない |
| 旧secret一致件数 | 想定した併用終了後も旧versionだけで通る | 送信側切替、別endpoint、旧worker、遅延配送 | 対象を特定し、旧secret失効を延期または漏えい時は隔離 |
| 配送件数差 | 送信側成功件数と受信保存件数が一致しない | 非2xx、timeout、rate limit、proxy、再送 | event IDで再送・補正し、欠落範囲を確定 |
| 重複副作用 | 通知、請求、CRMタスクが二重に作られる | 冪等性キー、event ID、再送時の処理順 | 重複処理を停止し、業務データを補正 |
| 処理遅延 | 2xxは返るがキューが滞留する | worker、外部API、DB、queue depth | 受付と処理本体を分け、滞留分を安全に再処理 |
| 不審な旧secret利用 | 失効前後に想定外IP・時刻・eventで一致する | 送信サービスの配送記録、アクセスログ、影響操作 | 事故対応へ切り替え、旧secret即時失効と影響調査 |
計画交換で新secretの設定不備が見つかった場合は、送信側が安全に旧secretへ戻せる仕様で、かつ旧secretに漏えい疑いがないときだけ一時的な復旧を検討できます。漏えい対応では、疑わしい旧secretへ戻してはいけません。新secretを再発行し、必要ならWebhookの副作用を一時停止してイベントを安全なキューへ貯め、復旧後にevent IDで再処理します。利用終了時の認証情報停止は、AI・SaaSサービスの終了計画の権限撤去と同じく、入口、認証情報、データ、監査証跡を一組で閉じます。
secretがリポジトリ、ログ、チャット、チケット、画面共有へ露出した場合は、値の削除だけで終えません。旧secretを失効し、露出時刻から失効までの配送を調べ、偽イベントが業務処理へ到達していないか確認します。報告窓口と初動の担当を事前に決める考え方は、security.txtと脆弱性報告対応でも整理できます。
段階適用と停止条件を設ける方法は、セキュリティ設定をいきなり強制しないCSP Report-Onlyの段階導入にも通じます。ただし署名検証はReport-Onlyにして未検証イベントを通すのではなく、新旧どちらの正規secretで一致したかを観測し、一致しないイベントは拒否する点が異なります。
仕様確認に使える公式資料
- GitHub Docs:Validating webhook deliveries:HMAC-SHA256、
X-Hub-Signature-256、raw payload、定数時間比較、secretの安全な保管を確認できます。 - GitHub Docs:Redelivering webhooks:失敗配送は自動再送されず、過去3日間の配送を手動再送できる条件を確認できます。
- Stripe Docs:Receive Stripe events in your webhook endpoint:raw bodyによる署名検証、最長24時間のsecret併用、複数署名、timestampと再送時の署名生成を確認できます。
- OWASP Secrets Management Cheat Sheet:生成、ローテーション、失効、期限、最小権限、監査、自動化を一つのライフサイクルとして設計する基準です。
よくある質問
Webhook署名シークレットはいつローテーションすべきですか?
定期交換日だけでなく、担当者・委託先・保管基盤の変更、不要な閲覧権限の発見、ログやリポジトリへの露出、漏えい疑い、連携終了を契機にします。頻度はsecretが守る業務の重要度、閲覧主体、再発行の容易さ、送信サービスの仕様に合わせ、台帳へ次回確認日を記録します。
新旧シークレットをどの期間併用しますか?
固定の日数ではなく、送信サービスの併用上限、最大配送遅延、再送方法、全受信instanceへの反映、実配送と再送テストの完了で決めます。Stripeは旧secretの失効を最長24時間遅らせられますが、他サービスへその時間を流用しません。漏えい疑いでは併用を最小化します。
正規イベントの取りこぼしをどう検知しますか?
送信側の配送ログと受信側の保存ログをevent IDで突き合わせ、署名不一致、非2xx、timeout、キュー滞留、業務レコード反映件数を確認します。テスト配送だけでなく本番の低リスクな正規イベントと再送を使い、入口から最終処理まで追跡します。
旧シークレットを廃止できる条件は何ですか?
新secretで実配送と再送が成功し、旧secretでしか通らない正規配送がゼロで、全instanceが新設定を読み、業務件数の差異がないことを確認します。その後、送信側の旧secret、受信側の候補、Secret Managerの旧version、不要な閲覧権限、古いCI変数を順に停止します。
環境変数に保存していれば十分ですか?
環境変数はコードへの直書きを避けられますが、配布、閲覧権限、version、監査、失効を自動で解決するわけではありません。Secret Managerなどの専用保管先を正本にし、実行主体だけが取得できるようにし、環境ごと・endpointごとにsecretを分離します。
署名エラーが増えたら一時的に検証を外してよいですか?
外してはいけません。raw bodyの変更、header、secret version、設定反映、時刻同期を確認し、一致しないイベントは隔離します。業務継続が必要なら未検証イベントを処理するのではなく、検証前のraw requestを安全な隔離領域へ保存し、原因解消後に正規性を確認して再処理します。
まとめ
Webhook署名シークレットのローテーションは、新しい値へ置き換えるだけの作業ではありません。対象をendpoint単位で棚卸しし、新secretを専用保管先へ置き、受信側を先に新旧対応へ更新してから送信側を切り替えます。実配送、再送、冪等性、最終業務処理まで確認し、旧secretでしか通らない正規配送がなくなってから失効します。
併用期間と再送方法はサービスごとに異なります。公式仕様を確認し、secret値をログへ出さずversion別の一致件数だけを監視してください。漏えい疑いでは旧secretへ戻らず、新しいsecretへの緊急交換、未検証イベントの隔離、影響調査、不要権限の撤去まで一つの事故対応として閉じることが重要です。