🔍

WAFでブロックしたはずなのに200 OKが返ってくる謎を解く

に公開

はじめに

AWS CloudFront と WAF を組み合わせてセキュリティを強化していたところ、奇妙な現象に遭遇しました。

「WAF でリクエストを BLOCK しているはずなのに、なぜか 200 OK が返ってくる...?」

ログを見ると確かに WAF で BLOCK されているのに、ブラウザには白いページが表示され、レスポンスコードは 200。一体何が起きているのでしょうか?

この記事では、この謎現象の原因と仕組みを詳しく解説します。

🔍 問題の現象

観測された症状

  • WAF ログ: action: "BLOCK"と記録されている
  • CloudFront ログ: sc-status: "200"となっている
  • ブラウザ: 白いページが表示される(コンテンツは空)
  • Lambda@Edge: なぜか実行されている

設定状況

CloudFront設定:
  - カスタムエラーページ設定あり
    - HTTP error code: 403 Forbidden
    - Response page path: /index.html
    - HTTP Response code: 200 OK  # ←ここが重要

AWS WAF設定:
  - IPアドレス制限ルールでBLOCK
  - カスタムレスポンス設定なし

Lambda@Edge設定:
  - Viewer Requestイベントで動作
  - Cognitoリダイレクト処理

📊 ログから見る時系列

実際のログタイムスタンプを分析すると、以下の順序で処理されていることが判明:

CloudFront がリクエスト受信
↓
WAF がリクエストをBLOCK(ログ記録時刻)
CloudFrontがカスタムエラーページ処理開始
↓ 403→200変換により内部リクエスト生成
Lambda@Edge (Viewer Request) 実行開始
CloudFront最終レスポンス記録(200 OK)
Lambda@Edge処理完了

待って...WAF で BLOCK されてるのになんで Lambda@Edge が動いてるの?

🔧 謎解き:CloudFront カスタムエラーページの仕組み

真相

実は、CloudFront には 「オリジンからの 403 エラー」と「WAF からの 403 エラー」を区別できない という仕様があります。この区別できない仕様のため、WAF でブロックされたリクエストでも内部リクエストで Lambda@Edge が実行され、結果として 200 OK が返されることになります。

CloudFront は、オリジンから返された HTTP ステータスコード 403 と AWS WAF がリクエストをブロックしたときに返された HTTP ステータスコード 403 を区別できません

AWS 公式ドキュメントより

処理の流れ

  1. WAF が 403 エラーを返す

    • リクエストが IP アドレス制限ルールにヒット
    • WAF: action: "BLOCK"
  2. CloudFront が「これはオリジンからの 403 だ」と誤認

    • 実際は WAF からの 403 だが、CloudFront には区別できない
  3. カスタムエラーページ設定が動作

    • 設定: 403 → 200 OK + /index.html
    • この「403→200 変換」設定が新しい内部リクエストを生成
  4. 内部リクエストで Lambda@Edge が実行される

    • /index.htmlへリクエスト
    • Viewer Request イベントで Lambda@Edge が動作
  5. 結果として 200 OK が返される

    • 実際のコンテンツは取得できないため空ページ

📋 2 つの動作パターン比較

パターン A: CloudFront カスタムエラー設定のみ

パターンA: CloudFrontカスタムエラー設定のみ

サービス 動作 ログ結果
WAF リクエストを BLOCK action: "BLOCK", responseCodeSent: null
CloudFront カスタムエラー処理実行 sc-status: "200", x-edge-result-type: "Error"
Lambda@Edge 実行される Cognito リダイレクト処理が動作
エンドユーザー 200 OK(空ページ)受信 白いページが表示される

用途: SPA(Single Page Application)対応

パターン B: WAF カスタムレスポンス + CloudFront カスタムエラー併用

パターンA: CloudFrontカスタムエラー設定のみ

サービス 動作 ログ結果
WAF カスタムレスポンス返却 action: "BLOCK", responseCodeSent: 403
CloudFront WAF レスポンスをそのまま転送 sc-status: "403", x-edge-result-type: "Error"
Lambda@Edge 実行されない ログなし
エンドユーザー 403 Forbidden 受信 アクセス拒否ページ

用途: セキュリティ重視

🏗️ AWS 公式の優先順位ルール

AWS 公式ドキュメントによると、レスポンスの優先順位は以下の通り:

  1. WAF カスタムレスポンス(最優先)
  2. CloudFront カスタムエラーページ
  3. WAF デフォルトレスポンス(403 Forbidden)

今回の現象は、WAF カスタムレスポンス未設定のため、CloudFront カスタムエラーページが動作

⚡ 解決策と推奨設定

🎯 SPA + セキュリティを両立させたい場合

  1. WAF カスタムレスポンス設定
    • 不正アクセスのブロックは確実に 403 を返す
  2. CloudFront カスタムエラーページ
    • S3 オリジンの 404/403(ルーティング用)は 200+index.html を返す
  3. Lambda@Edge での追加制御
    • リクエストヘッダーや IP アドレスでより細かい判定

💰 コスト最適化を重視する場合

WAF カスタムレスポンスを設定することで、不要な Lambda@Edge 実行を防げます。

⚠️ 注意点

  • セキュリティ考慮: WAF で BLOCK されても Lambda@Edge が実行される可能性があるため、Lambda@Edge 内でもセキュリティ制御を実装する
  • コスト面: 不要な Lambda@Edge 実行を避けたい場合は、WAF カスタムレスポンスを設定する

🔍 デバッグのポイント

この現象に遭遇した際の調査方法:

  1. WAF ログの確認

    • responseCodeSentnullなら、CloudFront カスタムエラーが動作する可能性あり
  2. CloudFront ログの確認

    • x-edge-result-type: "Error"なのにsc-status: "200"なら、カスタムエラーページが動作
  3. Lambda@Edge ログの確認

    • WAF で BLOCK されているのにログがあるなら、内部リクエストが生成されている
  4. リクエスト ID の突合

    • 同一request-idでサービス間のログを追跡

まとめ

「WAF で BLOCK したのに 200 OK が返ってくる」 という現象は、CloudFront の仕様による正常な動作でした。

キーポイント:

  • CloudFront は WAF の 403 とオリジンの 403 を区別できない
  • 「403→200 変換」設定が内部リクエストを生成する
  • WAF カスタムレスポンス設定で動作を制御可能

この仕様を理解することで、SPA 対応とセキュリティ対策を適切に両立できるようになります。

同じような現象に遭遇した方の参考になれば幸いです!


参考資料

Virtual Craft Tech Blog

Discussion