【ITニュース解説】A valid webhook signature doesn't mean you haven't handled it already
2026年10月24日に「Dev.to」が公開したITニュース「A valid webhook signature doesn't mean you haven't handled it already」について初心者にもわかりやすく解説しています。
ITニュース概要
Webhookでは、署名が有効でもリクエストが重複する可能性がる。署名検証は改ざんや有効期限をチェックするが、処理済みかは見ない。同じイベントの二重処理を防ぐには、別途データベースで処理済みか確認する仕組みが必要。署名チェックと重複対策は分けて考えよう。
ITニュース解説
Webhookは、現代のWebサービス連携において非常に重要な技術だ。これは、あるシステムで特定のイベントが発生した際に、その情報を別のシステムへリアルタイムに自動で通知する仕組みを指す。例えば、オンラインストアで商品が購入された際に、その購入情報を在庫管理システムや顧客管理システムへすぐに送る、といった使われ方が一般的である。Webhookはシステムの連携を効率化し、ユーザー体験を向上させるために広く利用されている。
しかし、この便利なWebhookを安全に使うためには、いくつかのセキュリティ対策が不可欠だ。その一つが「署名検証」である。Webhookで送られてくるデータは、悪意のある第三者によって改ざんされたり、なりすましによって不正な情報が送られてきたりする可能性がある。署名検証は、送られてきたデータが通信中に改ざんされていないか、そして本当に信頼できる送信元から送られてきたものなのかを確認するためのセキュリティメカニズムだ。具体的には、送信元がデータを特定の秘密鍵を使って暗号化(ハッシュ化)し、その結果生成される「署名」をデータと一緒に送る。受信側は同じ秘密鍵と受け取ったデータを使って同様のハッシュ値を計算し、送られてきた署名と一致するかどうかを検証する。一致すれば、データは改ざんされておらず、信頼できる送信元からのものであると判断できる。
今回の記事の核心は、「署名が有効であっても、それが重複したイベントでないとは限らない」という点にある。つまり、署名検証が成功したからといって、そのイベントが「初めて処理する新しいイベントである」とは限らない、ということだ。署名検証は「データが正当であること」は証明するが、「イベントが唯一無二であること」は保証しないのである。
なぜこのような事態が起こるのか。Webhookはネットワーク経由でデータをやり取りするため、一時的なネットワークの遅延や、送信元のシステムが確実なデータ送信のために複数回同じイベントを再送するメカニズムを持っている場合がある。結果として、受信側のシステムには同じ内容のWebhookが複数回届くことがあるのだ。もし、システムがこのような重複したイベントを適切に処理できないと、深刻な問題が発生する可能性がある。例えば、商品の購入イベントが二重に処理されてしまえば、顧客に二重請求が発生したり、商品が二重に発送されてしまったりするかもしれない。会員登録のイベントが重複すれば、同じメールアドレスで複数のアカウントが作られてしまったり、エラーになったりするだろう。
このような重複イベントの問題を防ぐためには、署名検証とは別に「重複処理を防止する」ための仕組みを導入する必要がある。記事では、具体的な方法として「データベーストランザクションとユニーク制約の利用」や「適切なファイルロック」が挙げられている。データベーストランザクションとは、複数のデータベース操作を一つのまとまりとして扱い、その全てが成功するか、全てが失敗するかのいずれかになることを保証する仕組みだ。これにより、例えば「イベントを記録する」と「それに基づいて何か処理を行う」という一連の操作が、途中で中断されて中途半端な状態になることを防げる。ユニーク制約とは、データベースの特定のカラム(列)に、重複する値を登録できないようにする制約のことだ。イベントごとに一意のIDがある場合、そのIDにユニーク制約を設定すれば、同じIDを持つイベントが二度とデータベースに登録されることはなくなる。
記事では、Stripeという決済サービスのWebhookを例に、実際のコードを用いた検証結果が示されている。テスト用のverify_webhook関数は、送られてきたイベントのボディ(本体データ)、ヘッダー(メタデータ)、そして秘密鍵を使って署名を検証し、さらにイベントの発生時刻を示すタイムスタンプが、あらかじめ設定された有効期限内にあるかどうかも確認している。このテストでは、同じボディ、同じヘッダー、同じ秘密鍵で関数を二度呼び出しても、どちらも成功した。これは、verify_webhook関数が署名とタイムスタンプの正当性しか見ておらず、以前に同じイベントを処理したかどうかを記憶していないことを明確に示している。
また、意図的にイベントのボディにスペースを一つ追加したり、検証する時刻を有効期限外に設定したりすると、verify_webhook関数はイベントを拒否した。この結果は、署名検証がデータ改ざんの有無とタイムスタンプの有効性を正確にチェックしていることを証明している。しかし、これはあくまで「データが改ざんされておらず、最近送られてきたものか」をチェックしているのであって、「このイベントが過去に処理されたことがないか」という問いには答えていないのだ。
したがって、システム開発においては、署名検証と重複防止の処理を明確に区別し、それぞれ別の目的を持った独立したロジックとして設計・実装する必要がある。署名検証はデータの信頼性を保証し、重複防止はイベントの唯一性を保証する、と考えるべきだ。
記事では、ダウンロードリンクのヘルパー機能も別のチェックが必要であると指摘している。ダウンロードリンクが正当な署名を持っていること(つまり、リンクが改ざんされておらず、正規に発行されたものであること)は、署名検証によって確認できる。しかし、そのリンクが何回ダウンロードされたか、有効期限が切れていないか、あるいは返金処理が行われたためにダウンロードを停止すべきか、といった「業務ロジック」は、署名検証だけでは判断できない。これらは、それぞれ別のデータベースやシステムの状態を参照し、個別のロジックで処理する必要があるのだ。
システムエンジニアを目指す初心者は、この点から重要な教訓を得るべきだ。システムを設計する際には、「不正な署名のイベント」と「有効だが重複しているイベント」の両方の可能性を考慮し、それぞれに対して適切な防御策を講じる必要がある。これらは異なる種類の問題を解決するためのものであり、混同してしまうと、例えば二重の課金や誤ったデータ処理といった、利用者やビジネスに大きな損害を与える事態に繋がりかねない。テストを行う際も、「無効な署名」のテストと、「有効だが重複するイベント」のテストを、それぞれ独立したシナリオとして実施することが不可欠である。このように、各セキュリティ機能やビジネスロジックの責任範囲を明確に理解し、それらを適切に組み合わせることで、堅牢で信頼性の高いシステムを構築できる。