チャットボットのWebhook受信設計|署名検証・重複防止・再送対応
チャットボットWebhookを安全に受信するため、生ボディの署名検証、リプレイ対策、冪等化、短時間応答、再送・順序逆転の試験を解説します。
チャットボットのWebhookセキュリティは、署名を照合するだけでは確保できません。正規のイベントでも、送信元の再送や受信側の障害によって複数回届くことがあります。処理順序が入れ替わる場合もあります。そのまま業務処理へ渡すと、同じ利用者へ二回返信する、有人対応のチケットを二件作る、古いイベントによって現在の対応状態を巻き戻すといった事故につながります。
安全な受信処理には、提供元の最新仕様を確認したうえで、JSON解析前の生ボディを保持し、署名とイベントの鮮度を検証する設計が必要です。続いて、イベントIDによる重複確認、受領記録、HTTP応答、非同期の業務処理、結果記録までを一連の受信フローとして設計します。署名が正しいことと、業務上の副作用を一度だけ実行することは、別の要件として扱います。
チャットボットのWebhookで防ぐべき事故
最初に、受信口で防ぐ事故を五つに切り分けます。偽装イベントの受け入れ、署名済みリクエストの再利用、同一イベントの重複処理、正当なイベントの取りこぼし、到着順序の逆転です。それぞれ原因と対策が異なるため、署名検証を実装しただけで完了とは判断できません。
偽装への対策は、提供元と受信側だけが知るシークレットなどを用いた署名検証です。ただし、有効な署名が付いた同じリクエストを第三者が再利用するリプレイには、タイムスタンプやnonceの検査が必要です。また、提供元が障害時に同じイベントを正当に再送する場合は、そのイベントを受領しつつ、返信や登録を再実行しない冪等性が必要になります。
たとえば、同じメッセージイベントが二度届いても、受信回数に合わせて二回返信してはいけません。有人対応へ切り替えるイベントが重複した場合も、同じ問い合わせについて引き継ぎチケットを二件作らないようにします。一方、重複を恐れて再送を一律に拒否すると、最初の処理が途中で失敗していた場合に復旧できません。受信した事実と、業務処理が完了した事実を分けて記録する必要があります。
順序逆転も署名では防げません。「対応開始」の後に古い「未対応」イベントが届き、そのまま現在状態へ反映すると、担当者が対応中であるにもかかわらず、未対応へ戻る可能性があります。イベントの正当性、同一性、処理状態、更新順序を個別に判定することが、Webhook受信設計の出発点です。
実装前に提供元のWebhook仕様を確認する
Webhookの署名方式や再送条件は提供元ごとに異なります。特定サービスのヘッダー名、アルゴリズム、許容時間、応答期限を別のサービスへ流用してはいけません。不明な項目は推測せず、提供元の公式文書または公式SDKの最新版で確認します。SDKを使う場合も、どのバイト列を署名対象にするのか、生ボディをどのように渡すのかを確認してください。
| 確認項目 | 確認する内容 | 設計への反映 |
|---|---|---|
| 署名 | 署名対象、ヘッダー、アルゴリズム、文字コード、値の形式 | 生ボディの取得方法と検証処理を決める |
| 鮮度 | タイムスタンプ、nonce、許容時間の規定 | 期限外または再利用された要求の拒否条件を決める |
| 識別子 | イベントID、再送識別情報、正当な再送時に値が維持されるか | 冪等キーと重複判定を決める |
| HTTP応答 | 応答期限、成功とみなすステータス、再送条件 | 同期経路に置ける処理を限定する |
| 配送 | 再送間隔、回数、順序保証の有無 | 内部再試行と順序判定を設計する |
| 鍵管理 | 再発行方法、旧キーと新キーの併用可否、反映時期 | ローテーションと漏えい時の切替手順を決める |
確認結果は、参照した文書のURL、確認日、対象バージョンとともに設計書へ残します。提供元の仕様が変更されたとき、どの実装と試験を見直すべきか追跡しやすくするためです。LINE Messaging APIを利用する場合は、チャネルや秘密情報、Webhook URLの設定をLINE Messaging APIの設定手順で確認したうえで、署名検証や再送に関する最新の公式仕様を別途確認してください。
署名はJSON解析前の生ボディで検証する
署名検証には、受信したままの生のバイト列を使います。基本的な処理順序は、生ボディの取得、必要な署名ヘッダーの取得、提供元指定の方法による署名計算、定数時間比較、検証成功後のJSON解析です。検証前にパースしたオブジェクトを再びJSONへ変換しても、送信時と同じバイト列になるとは限りません。
JSONとして同じ意味でも、空白、改行、文字コード、エスケープ、キーの順序が変われば、署名対象のバイト列は変わります。Webフレームワークの本文解析ミドルウェアが先に動くと、生ボディを取得できなくなる場合もあります。実装前に、Webhookのルートだけで生ボディを保持できる場所と、本文サイズを制限する場所を確認します。
比較処理には、利用する言語や公式SDKが提供する定数時間比較を使います。単純な文字列比較では、比較が終了する位置によって処理時間に差が出る可能性があります。また、署名値の形式変換やデコードに失敗した要求は、業務処理へ渡さず、検証失敗として扱います。受信した署名値やシークレットそのものをエラーログへ出力してはいけません。
検証に成功して初めてJSONを解析し、必須フィールドとイベント種別を確認します。署名が正しくても、未対応のイベント種別や必須値が欠けたデータを無条件に処理してはいけません。署名不正とデータ形式不正はログ上で区別しますが、外部への応答本文から内部情報を推測されないようにします。
タイムスタンプ・nonce・イベントIDの役割を分ける
タイムスタンプ、nonce、イベントIDは、似て見えても用途が異なります。タイムスタンプは、要求が許容時間内に作られたかを確認する値です。nonceは、一度使われた値の再利用を検出します。イベントIDは、同じ業務イベントを複数回処理しないための識別子です。いずれか一つを保存すれば、ほかが不要になるわけではありません。
タイムスタンプを検査する場合は、提供元が指定する単位と署名対象への含め方を確認します。受信サーバーの時計が大きくずれていると、正当な要求を期限外として拒否してしまうため、時刻同期の監視も必要です。具体的な許容時間は独自に決め打ちせず、提供元の規定と業務上の遅延を踏まえて決めます。
nonceが提供される場合は、受信済みの値を一定期間保存し、同一値が再び使われたときの扱いを定めます。ただし、提供元がnonceを発行しないサービスで、架空のフィールドを前提にした実装はできません。イベントIDについても、正当な再送時に同じ値が維持されるかを公式仕様で確認します。
提供元に安定したイベントIDがない場合、代替キーは「正当な再送で同じ値を再現できるか」を基準に選びます。本文ハッシュだけでは、異なるイベントの本文が同じ場合や、再送時に配送情報だけが変わる場合に判定を誤るおそれがあります。複数の項目を組み合わせる場合は、どの条件で同一イベントとみなすかを文書化し、同時受信試験で確認します。
イベントIDと処理状態で冪等性を確保する
冪等性台帳には、少なくとも提供元、イベントID、受信日時、処理状態、試行回数、最終エラー、完了日時を記録します。複数の提供元で同じ文字列のイベントIDが使われる可能性を考え、提供元とイベントIDの組み合わせを一意に扱います。内部相関IDも付けると、受信ログ、キュー、ワーカー、外部APIの記録をまとめて追跡できます。
処理状態は、未処理、処理中、完了、再試行待ち、要確認など、運用判断につながる区分にします。「失敗」だけでは、再試行してよいのか、外部への登録がすでに成功しているのかを判断できません。状態ごとに、次に実行できる処理、更新できる担当、タイムアウト後の移行先を決めます。
| 現在の状態 | 同じイベントを受信した場合 | 判断 |
|---|---|---|
| 未処理 | 既存レコードを使う | 新しい台帳行を増やさず処理対象にする |
| 処理中 | 新しい実行を開始しない | 処理期限を超えていないか、ワーカーが稼働しているかを確認する |
| 完了 | 副作用を再実行しない | 受領済みとして応答する |
| 再試行待ち | 再試行予定へ統合する | 同じ処理を並行起動しない |
| 要確認 | 自動実行を増やさない | 外部処理の結果を確認してから決める |
同じイベントが同時に届く場合に備え、一意制約や原子的な更新を利用し、二つの受信処理がともに新規イベントと判断しないようにします。イベント記録には成功したものの、キューへ投入する前に障害が起きる場面も検討します。記録と投入を同じ更新単位で扱える仕組み、または台帳から未投入分を回収する仕組みを用意し、取りこぼしを防ぎます。
CRM更新を伴う場合は、受信境界の先にあるデータ更新方法をチャットボットとCRMの連携設計で整理できます。予約登録のように二重書き込みの影響が大きい処理では、チャットボットと予約システムの連携方法も参照し、WebhookのイベントIDだけでなく、連携先の予約識別子も確認してください。
受領応答と業務処理を分離する
同期経路には、署名と鮮度の検証、必要な形式確認、イベントの受領記録までを置きます。受領できたイベントには、提供元が成功とみなすHTTP応答を期限内に返します。返信文の生成、CRM更新、予約登録、有人引き継ぎなど、時間がかかる処理はキューやワーカーへ渡します。
ここで重要なのは、Webhookへの2xx応答が業務処理の完了を意味するとは限らない点です。受領を記録し、再処理できる状態にしたことを送信元へ伝えます。その後の成功や失敗は、内部の処理状態で管理します。業務処理が完了するまでHTTP接続を保持すると、送信元のタイムアウトと再送によって、同じ処理が並行して動きやすくなります。
| 失敗箇所 | 外部応答の考え方 | 復旧方法 |
|---|---|---|
| 署名または鮮度の検証 | 正当な受領として扱わない | 再処理せず、失敗の急増を監視する |
| 受領記録の保存 | 受領済みと応答しない | 提供元の仕様に沿った再送を想定する |
| キュー投入 | 受領記録との整合性で判断する | 台帳から未投入イベントを回収する |
| 業務処理 | 受領後であればHTTP応答と切り離す | 状態とエラー種別に基づいて内部再試行する |
どのHTTPステータスで再送されるのか、どの時点まで応答を待つのかは、提供元の仕様で確認します。保存失敗を成功扱いにすると取りこぼしが発生します。一方、業務処理に失敗するたびに送信元へ再送させると、重複実行が増えます。外部再送を期待する範囲と、内部再試行で復旧する範囲をあらかじめ分けてください。
再送・部分失敗・順序逆転を状態遷移で扱う
処理完了済みのイベントが再送された場合は、成功扱いで応答し、副作用を再実行しません。処理中の重複では新しいワーカーを増やさず、既存処理が期限を超えていないかを確認します。再試行待ちのイベントは、設定した上限と間隔に従って内部再試行へ回します。入力不備や権限不足など、同じ条件で繰り返しても成功しないエラーは自動再試行から外します。
判断が難しいのは、外部APIへの登録が成功した直後に、完了状態の保存だけが失敗する場面です。単純に再実行すると二重登録になるため、連携先APIが冪等キーや処理結果の照会手段を提供しているか確認します。利用できる場合は、イベントIDなどと対応付けます。利用できない場合は「要確認」へ移し、登録先の状態を確認してから再実行の可否を判断します。
順序に意味があるイベントでは、提供元の時刻や連番を利用できるか確認します。到着時刻だけを業務上の順序とみなすと、ネットワーク遅延や再送によって誤判定します。「対応開始」の後に古い「未対応」が届いた場合は、現在の版や更新時刻と比較し、古いイベントで状態を上書きしないようにします。順序保証がない場合は、現在状態から許可する遷移をあらかじめ定義する方法も有効です。
キュー停止中にイベントが滞留した場合は、復旧後にWebhookを手作業で一律に再送するのではなく、台帳の状態を基準に未完了分だけを抽出します。処理中のまま期限を過ぎたイベント、再試行待ち、要確認を分け、副作用の実行状況を確かめます。自動復旧できない条件は、チャットボットの運用ルールと結び付け、有人対応へ移す基準と担当者を決めておきます。
シークレットを保管し、安全にローテーションする
署名用シークレットは、ソースコード、平文の設定ファイル、ログ、エラー画面へ残しません。実行環境の秘密情報管理機能などに保管し、Webhook検証処理に必要な範囲だけで参照させます。参照権限、再発行権限、本番反映権限は、担当範囲に応じて分けます。
ローテーション前には、提供元が旧キーと新キーの併用を許すか、再発行時に旧キーが即時失効するかを確認します。併用できる場合は、新キーを受信側へ配布し、両方を検証候補に加えます。その後、テストイベントの検証成功を確認してから旧キーを失効します。即時切替しかできない場合は、変更担当、実施時刻、切り戻しの可否、監視方法を合意してから作業します。
漏えいが疑われる場合は、通常の定期交換と分けて扱います。影響する受信口と環境を特定し、新しいキーへ切り替え、旧キーを失効させ、不審なイベントや署名失敗の状況を確認します。実施者、承認者、切替時刻、確認結果は監査記録へ残します。再発行や本番設定変更の権限分離については、チャットボットのアクセス制御設計も参照してください。
ログと監視で追跡性を確保する
ログには、提供元、イベントID、受信時刻、署名検証の成否、処理状態、試行回数、HTTP応答コード、所要時間、内部相関IDを記録します。イベントIDが個人情報と結び付く場合は、参照できる担当者と保存期間も決めます。ログだけでなく、冪等性台帳とキューの状態も関連付け、どの段階で停止したか確認できるようにします。
シークレット、署名値、認証トークン、不要な会話本文は記録しません。エラー解析を理由にリクエスト全体を常時保存すると、会話内容や個人情報を過剰に保持することになります。必要なフィールドを限定し、本文が必要な例外調査ではアクセス権限と削除期限を定めます。Webhook以外も含めた情報管理の方針は、セキュリティポリシーの考え方と整合させます。
監視対象は、署名失敗の急増、処理待ち件数の滞留、処理中状態の期限超過、再試行上限への到達、順序不整合、要確認状態の増加です。単発エラーをすべて通知するのではなく、対応が必要な条件と通知先を決めます。アラートを受けた担当者が、相関IDから台帳、キュー、外部処理の結果を確認できる手順も用意します。
障害注入テストで受信設計を検証する
公開前に加え、署名方式、キュー、連携先、鍵管理を変更した後は、正常系だけでなく異常系も試します。各試験では、HTTP応答、台帳状態、副作用の実行回数、再試行の有無、アラート、有人確認への移行を記録します。「エラーになった」という結果だけで終えず、期待する状態遷移と一致したかを確認します。
| 試験 | 操作 | 期待結果 |
|---|---|---|
| 本文改変・不正署名 | 署名後に本文または署名値を変える | JSON処理や業務処理へ進まず、失敗として記録される |
| 期限外・nonce再利用 | 古い時刻または使用済みnonceで送る | 提供元仕様に基づいて拒否され、副作用が起きない |
| 同時重複 | 同じイベントIDを並行送信する | 台帳が一意に保たれ、返信や登録が一度だけ行われる |
| 受領保存の失敗 | データストアを停止する | 受領済みと誤応答せず、回復後の再送を処理できる |
| ワーカー停止 | 受領後にキューまたはワーカーを止める | 受領記録が残り、復旧後に未完了分だけ再処理される |
| 部分失敗 | 外部API成功後の完了記録を失敗させる | 自動で二重登録せず、照会または要確認へ移る |
| 順序逆転 | 新しい状態の後に古いイベントを送る | 現在状態が古い値へ巻き戻らない |
| 鍵切替 | 提供元仕様に沿って新旧キーを切り替える | 新キーで検証でき、旧キー失効後は旧署名を受け入れない |
試験環境では、実在する利用者への返信や本番CRMへの登録が起きないように、送信先を分離します。本番相当の同時実行や障害復旧を確認する場合も、テストイベントを識別できるようにします。試験で見つかった不整合は、コードだけでなく、状態遷移図、運用手順、監視条件にも反映します。
導入時に決めるWebhook受信チェックリスト
- 提供元の公式仕様について、参照先、確認日、対象バージョンを記録したか
- 署名対象の生ボディをJSON解析前に取得し、定数時間比較で検証するか
- タイムスタンプ、nonce、イベントIDの有無と用途を切り分けたか
- 正当な再送で同じ値になる冪等キーを決めたか
- 提供元、イベントID、処理状態、試行回数、最終エラーを台帳へ記録するか
- 受領記録までの同期処理と、業務処理を分離したか
- 保存失敗、処理失敗、重複受信ごとのHTTP応答方針を決めたか
- 再試行できるエラー、上限到達後の扱い、有人確認への移行条件を決めたか
- 順序逆転時の比較値と、許可する状態遷移を決めたか
- 外部APIの冪等キーまたは処理結果照会の有無を確認したか
- シークレットの保管、参照権限、再発行、旧キー失効の手順を決めたか
- ログからシークレット、署名値、認証トークン、不要な会話本文を除外したか
- 監視条件、通知先、復旧責任者、定期試験日を決めたか
未決定の項目が残る場合は、本番公開前に開発担当と運用担当の担当範囲を明確にします。Webhook受信仕様、冪等性台帳、有人対応との境界を自社だけで確定しにくい場合は、ここで整理した要件をもとに、Socratesの導入可否や運用方法をご確認・ご相談ください。実際に利用できる連携方法や対応範囲は、対象環境と要件を確認したうえで判断します。
よくある質問
署名検証に成功すれば、同じイベントを二回処理することはありませんか
署名検証は、送信元と本文の改変有無を確認する仕組みであり、処理回数は制御しません。正当な再送にも有効な署名が付くため、イベントIDと処理状態を台帳へ記録し、完了済みの副作用を再実行しない仕組みが必要です。
Webhookへ2xxを返した時点で、返信や登録も完了している必要がありますか
必ずしも同時に完了させる必要はありません。提供元の仕様を確認し、署名検証と受領記録までを同期経路に置き、時間がかかる業務処理は非同期化します。2xxが何を意味し、どの失敗で再送されるかは提供元ごとに確認してください。
イベントIDが提供されない場合は、本文ハッシュを使えばよいですか
本文ハッシュだけで決めるのは避けます。異なるイベントでも本文が同じ場合があり、再送時に一部の配送情報が変わる場合もあるためです。正当な再送時に同じ値を再現できる項目を公式仕様で確認し、必要であれば複数項目を組み合わせて、同一性の判断条件を文書化します。
再試行できないエラーはどのように扱いますか
入力不備、権限不足、外部処理の成否が不明な状態などは、同じ処理を繰り返しても解決しない場合があります。自動再試行から「要確認」へ移し、外部システムの登録状況や現在状態を確認してから、再実行または有人対応を判断します。