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 を区別できません
処理の流れ
-
WAF が 403 エラーを返す
- リクエストが IP アドレス制限ルールにヒット
- WAF:
action: "BLOCK"
-
CloudFront が「これはオリジンからの 403 だ」と誤認
- 実際は WAF からの 403 だが、CloudFront には区別できない
-
カスタムエラーページ設定が動作
- 設定:
403 → 200 OK + /index.html
- この「403→200 変換」設定が新しい内部リクエストを生成
- 設定:
-
内部リクエストで Lambda@Edge が実行される
-
/index.html
へリクエスト - Viewer Request イベントで Lambda@Edge が動作
-
-
結果として 200 OK が返される
- 実際のコンテンツは取得できないため空ページ
📋 2 つの動作パターン比較
パターン 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 カスタムエラー併用
サービス | 動作 | ログ結果 |
---|---|---|
WAF | カスタムレスポンス返却 |
action: "BLOCK" , responseCodeSent: 403
|
CloudFront | WAF レスポンスをそのまま転送 |
sc-status: "403" , x-edge-result-type: "Error"
|
Lambda@Edge | ❌ 実行されない | ログなし |
エンドユーザー | 403 Forbidden 受信 | アクセス拒否ページ |
用途: セキュリティ重視
🏗️ AWS 公式の優先順位ルール
AWS 公式ドキュメントによると、レスポンスの優先順位は以下の通り:
- WAF カスタムレスポンス(最優先)
- CloudFront カスタムエラーページ
- WAF デフォルトレスポンス(403 Forbidden)
今回の現象は、WAF カスタムレスポンス未設定のため、CloudFront カスタムエラーページが動作
⚡ 解決策と推奨設定
🎯 SPA + セキュリティを両立させたい場合
-
WAF カスタムレスポンス設定
- 不正アクセスのブロックは確実に 403 を返す
-
CloudFront カスタムエラーページ
- S3 オリジンの 404/403(ルーティング用)は 200+index.html を返す
-
Lambda@Edge での追加制御
- リクエストヘッダーや IP アドレスでより細かい判定
💰 コスト最適化を重視する場合
WAF カスタムレスポンスを設定することで、不要な Lambda@Edge 実行を防げます。
⚠️ 注意点
- セキュリティ考慮: WAF で BLOCK されても Lambda@Edge が実行される可能性があるため、Lambda@Edge 内でもセキュリティ制御を実装する
- コスト面: 不要な Lambda@Edge 実行を避けたい場合は、WAF カスタムレスポンスを設定する
🔍 デバッグのポイント
この現象に遭遇した際の調査方法:
-
WAF ログの確認
-
responseCodeSent
がnull
なら、CloudFront カスタムエラーが動作する可能性あり
-
-
CloudFront ログの確認
-
x-edge-result-type: "Error"
なのにsc-status: "200"
なら、カスタムエラーページが動作
-
-
Lambda@Edge ログの確認
- WAF で BLOCK されているのにログがあるなら、内部リクエストが生成されている
-
リクエスト ID の突合
- 同一
request-id
でサービス間のログを追跡
- 同一
まとめ
「WAF で BLOCK したのに 200 OK が返ってくる」 という現象は、CloudFront の仕様による正常な動作でした。
キーポイント:
- CloudFront は WAF の 403 とオリジンの 403 を区別できない
- 「403→200 変換」設定が内部リクエストを生成する
- WAF カスタムレスポンス設定で動作を制御可能
この仕様を理解することで、SPA 対応とセキュリティ対策を適切に両立できるようになります。
同じような現象に遭遇した方の参考になれば幸いです!
Discussion