はじめに
CloudFront + Lambda@EdgeでのBasic認証 + WAFでのアクセス制御 という構成で、「エラー403より先にベーシック認証のポップアップが出る?」という現象に遭遇しました。
これについての原因と対処方法をメモとしてまとめておきます。
本記事で扱う内容
- CloudFrontでIP制限(WAF)とBasic認証(Lambda@Edge)を併用したときの問題
- ベーシック認証がWAFの前に出てしまうように見える原因
- カスタムエラーレスポンス設定による403レスポンスのすり替わり
- 正しく403が出るようにするための設定変更
構成概要
以下のような構成で運用していました。
-
CloudFront
- S3(SPA)オリジン + API Gatewayオリジン
-
WAF
- 許可されたIP以外は BLOCK(403)
-
Lambda@Edge
- S3オリジンに対して Basic認証を実装(Vierwer Requestイベントで実行)
やりたいこと
- 許可IPのみアクセス許可(WAFがBLOCK)
- 通った人だけベーシック認証(Lambda@Edgeが401を返す)
起きていた問題
意図と反して、WAFで弾かれるはずのリクエストでもベーシック認証のポップアップが出るという現象が発生。
- WAFのログ → 正しく BLOCK(403)が記録されている
- ブラウザのDevTools → 401 Unauthorized が返っている
- Lambda@Edgeのログ → WAFブロックされたはずのリクエストが到達してる?
原因
CloudFrontの「エラーページ」の設定が原因
CloudFrontでは「WAFの403」も「オリジンからの403」も区別なく扱われるということで、
403 が返ってきたら → S3の index.html を返す
という設定があると
CloudFrontは「WAFの403」にも同じように反応し、エラーページを取得するために再度オリジンへアクセスしようとしてしまう。
→ その再アクセス時に、Lambda@Edge(Basic認証)を実行してしまい、401 を返してしまう
解決方法
カスタムエラーレスポンスの設定から403を削除
CloudFrontディストリビューションの「エラーページ」の設定から、以下を削除:
- HTTPエラーコード: 403
- レスポンスページのパス: /index.html などにリダイレクト
結果
- 非許可IP → 403 Forbidden(認証ポップアップが出ない)
- 許可IP → Lambda@Edgeで認証チェック → 未認証なら401(認証ポップアップが出る)
ということで理想通りの制御順序になりました。
補足メモ
- WAFは CloudFront の Viewer Request の直後に評価される
- CloudFrontは403の中身を見分けないので、カスタムエラー処理があるとすべて置き換えてしまう
まとめ
CloudFront + WAF + Lambda@Edge の組み合わせでWAFの403をそのまま返させるためには、CloudFrontで403のカスタムエラーレスポンスに設定してはいけない。