- SaaSにおけるWebhook配信の基本設計と配信フロー
- HMAC-SHA256署名によるペイロード検証の仕組み
- 指数バックオフによるリトライ戦略とDead Letter Queue
- 監査ログの設計パターンと記録すべき項目
- Slack通知やメール通知との連携パターン
SaaSにおけるイベント駆動の重要性
SaaSプロダクトでは、「PDF変換が完了した」「支払いが処理された」「ユーザーが操作した」など、さまざまなイベントが発生します。これらのイベントを外部システムに通知する仕組みがWebhookです。
FUNBREWでは自社SaaSプロダクト「FUNBREW PDF」の開発において、Webhook配信・HMAC署名・監査ログを含むイベント駆動アーキテクチャを実装してきました。本記事では、その設計パターンを解説します。
Webhook配信は「送って終わり」ではありません。署名の検証、失敗時のリトライ、配信状況の可視化、手動再送の仕組みまで含めて初めて「信頼できるWebhook基盤」と言えます。FUNBREW PDFの開発で、この一連の設計がいかに重要かを実感しました。
イベント駆動アーキテクチャの構成要素は大きく3つです。
- Webhook配信:イベント発生を外部に通知する
- 監査ログ:すべての操作とイベントを記録する
- 通知連携:Slackやメールなど内部向けの通知
これらはAPI認証設計とレート制限と合わせて、SaaSのAPI基盤を構成する重要な要素です。
Webhook配信の基本設計
Webhookは「イベント発生時に、登録されたURLにHTTP POSTリクエストを送信する」仕組みです。APIポーリングと比較した利点を整理します。
| 方式 | リアルタイム性 | サーバー負荷 | 実装コスト |
|---|---|---|---|
| Webhook(プッシュ) | 高い(即時通知) | 低い(イベント時のみ) | 配信側の設計が必要 |
| APIポーリング(プル) | 低い(ポーリング間隔依存) | 高い(空振りリクエスト多発) | 受信側が定期実行を実装 |
イベントの種類と設計
Webhookで通知するイベントは、「リソース.アクション」の形式で命名すると管理しやすくなります。
| イベント名 | 意味 |
|---|---|
| pdf.completed | PDF変換が完了 |
| pdf.failed | PDF変換が失敗 |
| batch.completed | バッチ処理が完了 |
| api_key.created | APIキーが新規発行された |
| usage.threshold_reached | 使用量が閾値に到達 |
イベント名の設計ポイントは以下の通りです。
- 過去形を使う:イベントは「すでに起きたこと」を通知するため、completedやcreatedのように過去形にする
- ドット区切り:リソースとアクションをドットで区切り、機械的にフィルタリングしやすくする
- 粒度の統一:似たリソースは同じ粒度で設計する(pdf.completedとbatch.completedのように)
ペイロードの設計
Webhook のペイロード(送信するJSONデータ)には、受信者が処理に必要な情報を含めます。
- イベントID:一意のID(重複排除に使用)
- イベントタイプ:上記のイベント名
- タイムスタンプ:イベント発生時刻(ISO 8601形式)
- リソースデータ:変更されたリソースの現在の状態
- メタデータ:テナントID、APIバージョン等
ペイロードには機密情報を含めないことが重要です。たとえば、APIキーの発行イベントでもキー本体は含めず、「APIキーが発行された」という事実とキーのIDのみを通知します。詳細が必要な場合は、受信者がAPIを呼び出して取得する設計にします。
HMAC署名によるセキュリティ
Webhookの受信側は、リクエストが正当な送信元から来たものかを検証する必要があります。これを実現するのがHMAC署名です。
HMAC-SHA256の仕組み
HMAC(Hash-based Message Authentication Code)は、共有シークレットを使ってメッセージの完全性と送信元の正当性を同時に検証する方式です。
- 送信側:ペイロード全体をシークレットキーでHMAC-SHA256ハッシュ化
- 送信側:ハッシュ値をHTTPヘッダー(例:
X-Webhook-Signature)に付与して送信 - 受信側:受信したペイロードを同じシークレットキーでHMAC-SHA256ハッシュ化
- 受信側:計算したハッシュ値とヘッダーの値をタイミングセーフに比較
一致すればペイロードは改ざんされておらず、正当な送信元からの通知であることが確認できます。
署名ヘッダーの設計
署名に関連するヘッダーは以下のように設計します。
| ヘッダー | 値の例 | 用途 |
|---|---|---|
| X-Webhook-Signature | sha256=a1b2c3d4... | HMAC-SHA256署名値 |
| X-Webhook-Timestamp | 1711805400 | 署名生成時のタイムスタンプ |
| X-Webhook-ID | evt_abc123 | イベントの一意ID |
タイムスタンプの検証(リプレイ攻撃対策)
HMAC署名だけではリプレイ攻撃(過去の正当なリクエストを再送する攻撃)を防げません。これを防ぐために、タイムスタンプの検証を追加します。
- 署名対象にタイムスタンプを含める(ペイロード+タイムスタンプのHMAC)
- 受信側でタイムスタンプが現在時刻から5分以内であることを検証
- 5分以上経過したリクエストは拒否
この仕組みにより、たとえ署名が有効でも、古いリクエストの再送は拒否されます。
シークレットキーの管理
Webhook用のシークレットキーの管理は、APIキー管理と同様のセキュリティレベルで行います。
- テナントごとに固有のシークレットを発行
- ローテーション機能を提供(新旧シークレットの並行運用期間を設ける)
- 管理画面で確認・再生成が可能
リトライ戦略の設計
Webhook配信は必ず成功するとは限りません。受信側のサーバーダウン、ネットワーク障害、タイムアウトなど、失敗するシナリオは多数あります。
指数バックオフによるリトライ
リトライ間隔は指数バックオフで設計します。即座に再送すると、障害中のサーバーにさらに負荷をかけてしまうためです。
| リトライ回数 | 待機時間 | 累計時間 |
|---|---|---|
| 1回目 | 15秒後 | 15秒 |
| 2回目 | 1分後 | 1分15秒 |
| 3回目 | 5分後 | 6分15秒 |
| 4回目 | 30分後 | 36分15秒 |
| 5回目(最終) | 2時間後 | 2時間36分15秒 |
リトライの設計ポイント
- 冪等性の確保:受信側が同じイベントを複数回受け取っても問題ないように、イベントIDによる重複排除を推奨
- タイムアウト設定:受信側の応答を待つタイムアウトは10〜30秒に設定。それ以上は失敗とみなす
- 成功判定:HTTPステータスコード2xx(200〜299)を成功とみなす。301/302リダイレクトは追跡する
- リトライ対象外:4xx系エラー(400、401、403等)は基本的にリトライせず即時失敗とする(受信側の設定ミスの可能性)。ただし429(Too Many Requests)は例外で、Retry-Afterヘッダーに従ってリトライする
Dead Letter Queue(DLQ)
最終リトライ後も失敗した配信は、Dead Letter Queue(DLQ)に格納します。DLQの設計ポイントは以下の通りです。
- 失敗した配信のペイロード、エラー内容、リトライ履歴を完全に保存
- 管理画面からDLQの内容を確認し、手動で再送可能にする
- DLQに蓄積された配信をアラートとして通知
- 一定期間(例:30日)経過した配信は自動削除
監査ログの設計
監査ログは、システム上のすべての操作を記録する仕組みです。セキュリティ調査、コンプライアンス対応、トラブルシューティングの基盤となります。
記録すべき項目
| 項目 | 内容 | 目的 |
|---|---|---|
| 誰が(Actor) | APIキーID、ユーザーID、IPアドレス | 操作者の特定 |
| いつ(Timestamp) | ISO 8601形式のタイムスタンプ | 時系列での追跡 |
| 何を(Resource) | リソースタイプ、リソースID | 操作対象の特定 |
| どうした(Action) | create、update、delete等 | 操作内容の特定 |
| 結果(Result) | success、failure、エラーコード | 成否の判定 |
| 変更内容(Changes) | 変更前後の値(diff) | 影響範囲の把握 |
監査ログとイベントの分離
監査ログとWebhookイベントは別々の仕組みとして設計します。
- 監査ログ:内部向け。すべての操作を網羅的に記録。保存期間は長め(1年以上)
- Webhookイベント:外部向け。利用者が必要とするイベントのみを通知
同じ操作(例:APIキーの発行)が両方をトリガーしますが、監査ログにはリクエスト元IP、ユーザーエージェント、処理時間など内部でしか必要ない情報も記録します。
ログの検索性
監査ログは記録するだけでなく、必要なときに素早く検索できることが重要です。
- テナント別の絞り込み:特定の顧客の操作履歴を一覧表示
- リソース別の絞り込み:特定のリソースに対する操作の時系列表示
- アクター別の絞り込み:特定のAPIキーやユーザーの全操作
- 期間指定:日時範囲での検索
通知連携のパターン
Webhookが外部システムへの通知であるのに対し、内部向けの通知もイベント駆動で実装します。
Slack通知の設計
Slack通知はBlock Kit形式で構造化すると、視認性が大幅に向上します。
- イベント種別ごとにフォーマットを定義:PDF完了はグリーン、失敗はレッドのアクセントカラー
- アクションボタン:通知から直接管理画面に遷移できるリンクを含める
- 通知レベルの制御:エラーのみ、全イベント、特定リソースのみなど、チャンネルごとに設定可能
メール通知の設計
メール通知は集約して送信するのがポイントです。イベントごとに個別メールを送ると、利用者の受信箱が溢れてしまいます。
- 即時通知:障害やセキュリティ関連の重要イベントのみ
- 日次ダイジェスト:処理結果のサマリーをまとめて1通
- 閾値通知:使用量が上限に近づいたときのアラート
疎結合設計のメリット
Webhook配信・監査ログ・通知をイベントを起点とした疎結合な設計にすることで、以下のメリットが得られます。
- 追加が容易:新しい通知先(Teams連携、LINE通知等)を追加しても、コアのビジネスロジックを変更しない
- 障害の局所化:Slack通知のAPIが落ちても、Webhook配信や本体の処理には影響しない
- テストの容易さ:各コンポーネントを独立してテスト可能
- スケーラビリティ:イベント処理をキューで非同期化し、負荷に応じてスケール
マルチテナントSaaSでは、テナントごとに異なるWebhook設定や通知設定を持つため、この疎結合設計が特に重要になります。
まとめ:イベント駆動アーキテクチャの設計指針
SaaSのイベント駆動アーキテクチャは、以下のポイントで設計します。
- Webhook配信:HMAC-SHA256署名、タイムスタンプ検証、指数バックオフのリトライ
- 監査ログ:誰が・いつ・何を・どうした・結果の5項目を網羅的に記録
- 通知連携:Slack Block Kit、メールダイジェスト、閾値アラートを組み合わせ
- 疎結合:イベントを起点に各コンポーネントを独立して動作させる
これらを組み合わせることで、外部連携に強く、運用で困らないSaaS基盤を構築できます。
FUNBREWでは、自社SaaSプロダクト「FUNBREW PDF」の開発で培った知見をもとに、システム開発をご支援しています。SaaS開発やAPI設計でお悩みの方は、ぜひお気軽にご相談ください。
この記事をシェア