油断は禁物!AWS WAFのすり抜けにご用心!

こんにちは、ひるたんぬです。

最近は急に冷え込んできましたね。秋、というより冬が来た、という感じです。
全く関係ない話ですが、なぜ「醤油(しょうゆ)」は油ではないのに、醤と書くのでしょうか。
ごま油、サラダ油、なたね油…分かります。でもなぜ…?

醤油の言葉の意味としては“醬(ヒシオ)を絞ってできた油(液)”です。
『漢字源 改訂第6版』(学研 2018年12月刊)の“油”(p.1055)によると、“するすると滑らかで通りのよい液体を示す”とあり、“油”は“液体”という意味で使われているようです。
引用…国立国会図書館「レファレンス協同データベース」| 醬油にはなぜ“油”という字が使われているのか。

油は油だけど、(私が認識しているような)油ではない…少し頭が混乱してしまいました。

さて、今回はタイトルにもありますように、「AWS WAFでブロックしていたはずなのに…見れてしまう!」となってしまう事例を簡単にご紹介します。

構成

今回はAmazon CloudFrontとAmazon S3を用いた静的コンテンツを公開する基盤を想定し、アクセス保護の目的でAWS WAFを前段に配置することにします。

検証

ここからは、実際に構築を行いながら検証をしていきます。

事前準備

まず静的コンテンツを公開するために、Amazon CloudFront ディストリビューションとAmazon S3 バケット、アクセス保護をするAWS WAFを作成します。
バケットの作成後、適当なコンテンツを用意して格納しておきましょう。

今回の検証では、404 Not Foundエラーを表示させたいため、S3のバケットポリシーにおいて、CloudFrontの当該ディストリビューションのListBucket権限を許可します。
関連サイト…aws re:Post – 画像が CloudFront からロードされないのはなぜですか?

一通り準備ができたら、アクセスしてコンテンツが表示されることを確認します。

無事に見れましたね。

続いて、存在しないアドレスにアクセスした際にコンテンツが存在しない旨を分かりやすく表示させます。
ここでは、CloudFrontのカスタムエラーレスポンスを設定し、404エラーに対して「notfound.html」を表示させるようにします。

設定が完了したら、実際に存在しないページにアクセスして確認をします。

しっかりエラーのメッセージが分かりやすく表示されていますね。

カスタムエラーレスポンスを設定しない場合、とてもシンプルなメッセージが表示されます。

WAF設定

ここからが本題です。
今現在の構成では最低限のアクセス保護のみが実施されています。
そこで、AWS WAFの設定を変更し、特定のIPアドレス以外からのアクセスを拒否するように設定します。

今回は検証目的のため、自端末に割り当てられているグローバルIPを拒否するよう設定しました。
これを実施することにより、不特定多数からのコンテンツ閲覧を制御することが可能です。
設定後、拒否されている環境から同じようにアクセスしてみると…
WAFによってしっかりブロックされていることを確認できました。
ブロックされない!という方は、IPv6で通信している可能性があるため、IPv6のアドレスをブロックするよう設定してみてください。

そして…事件発覚

ここで私は「この403のページももっと親切な案内にしたい!」と思うようになりました。
そこで一番最初に思いついたのは、先程404 NotFoundのときに設定した「カスタムエラーレスポンス」です。
これは403でも同様に設定できるため、403エラーに対しては「accessdenied.html」を表示させるよう設定をしました。

実際に設定を終えて、表示を確認してみると…

おぉ!なんか怖い画面ですが、しっかり日本語で表示されましたね!よしよし。。。

……ん?
既にお気づきの方もいらっしゃるかもしれませんが、この挙動、何かおかしくないですか?
先程設定したのはCloudFrontのカスタムエラーレスポンスで、403 ForbiddenのときにはS3の中に格納されている「accessdenied.html」を表示させるようにすること…
そして、今現在アクセスしているのは、AWS WAFにてブロックしている端末から…

なぜCloudFrontに設定し、S3に格納されているコンテンツが閲覧できてしまうのでしょうか??

調査

403エラー時のレスポンスである「accessdenied.html」以外は確かに表示できないようになっていますが、いかんせんWAFを通り越してCloudFront(S3)のコンテンツが閲覧できてしまっているのがとてもとても気になってしまったので、調べてみました。
すると、公式ドキュメントに以下のような記載がありました。

By default, when AWS WAF blocks a web request based on the criteria that you specify, it returns HTTP status code 403 (Forbidden) to CloudFront, and CloudFront returns that status code to the viewer. The viewer then displays a brief and sparsely formatted default message similar to the following:

Forbidden: You don't have permission to access /myfilename.html on this server.

You can override this behavior in your AWS WAF protection pack (web ACL) rules by defining custom responses. For more information about customizing response behavior using AWS WAF rules, see Sending custom responses for Block actions.

引用:AWS Docment | Common use cases for protecting CloudFront distributions with AWS WAF

日本語が機械翻訳で崩壊していたので英語を引用していますが、まとめると

  • WAFでブロックされると、403(Forbidden)をCloudFrontに対して返す
  • デフォルトのメッセージは上記のような表記となる(Forbidden: You don’t have…)
  • カスタム(エラー)レスポンスを設定することで、この設定を上書きすることができる

という動作になります。別のページにも分かりやすく整理されていました。

For web requests that AWS WAF blocks, the following shows the order of precedence.

  1. AWS WAF custom response – If the AWS WAF Block action has a custom response enabled, the protected resource sends the configured custom response back to the client. Any response settings that you might have defined in the protected resource itself have no effect.
  2. Custom response defined in the protected resource – Otherwise, if the protected resource has custom response settings specified, the protected resource uses those settings to respond to the client.
  3. AWS WAF default Block response – Otherwise, the protected resource responds to the client with the AWS WAF default Block response 403 (Forbidden).

引用:AWS Docment | Sending custom responses for Block actions

つまり、WAFでブロックされた場合は、

  1. AWS WAFに設定されたカスタムレスポンス
  2. 保護されたリソース(CloudFrontなど)で設定されているカスタムレスポンス
  3. AWS WAFに設定されたデフォルトレスポンス

の優先順位で表示がされるようです。これで納得ですね。

つまり、AWS WAFでアクセスをブロックしたと言っても、CloudFrontのコンテンツの内容を表示できるケースが仕様として存在する、ということですね。
もちろんカスタマイズしたレスポンスを提供できることはメリットになりますが、その設定を誤ってしまうと公開したくない(する予定のない)コンテンツを公開しかねないことになるので注意が必要です。

今回は、この事象を「AWS WAFのすり抜け」と名付けました。

深堀り

では、CloudFront(S3)のコンテンツを参照させることなくAWS WAFでブロックされたことを分かりやすく示す方法はないのでしょうか…?
引き続きドキュメントを見てみると、気になる表現がありました。

Responses that you customize using AWS WAF rules take precedence over any response specifications that you define in CloudFront custom error pages.
引用:AWS Docment | Common use cases for protecting CloudFront distributions with AWS WAF

AWS WAFでレスポンスをカスタマイズすることで、CloudFrontのカスタムエラーレスポンスよりも優先して表示させることができる、と言ったような記載があります。
おぉ!これですね。早速設定してみます。

該当するWAFの設定項目から「Custom response bodies」のタブを選択し、「Create custom response body」をクリックします。

名前と表示させる内容を設定します。
内容はJSONの他、HTMLやプレーンテキスト形式で設定ができるようなので、今回はHTMLで設定してみます。

登録する内容には、4KBの制限があります。

設定が終わったら、下部の「Save」をクリックします。
次に、既に設定されているWAFのルールから、IP制限に関するルールを編集します。
中段に記載の「Custom response – optional」を有効化し、レスポンスコードと、先程登録したエラーレスポンスを選択します。

AWS WAFで設定するカスタムレスポンスは、自分で定義したルールにのみ適用することができます。
ルールごとに適用が必要になるため、複数ルールを定義している場合は注意が必要です。
また、マネージドルールに対してカスタムレスポンスを設定する場合は、別途設定が必要となります。
参考:AWSブログ | AWS WAFのAWS マネージドルールの動作をカスタマイズする方法
設定が完了したので、改めてアクセスしてみましょう。

…まぁ、なんとも仰々しい画面ですね。
しっかりとWAFにて設定したカスタムレスポンスがCloudFrontのカスタムエラーレスポンスよりも優先して表示されていることを確認できました。

ブロック検知ルールにXSSやSQLインジェクションとありますが、違いますね。
今回はここの内容は本質的ではないのでご承知おきください。
(生成AIに作ってもらったら、この部分を決め打ちで記載してくれていました。)

おわりに

今回はAWS WAFを用いたコンテンツ公開基盤に対して、WAFがブロックしたときの挙動をご紹介いたしました。
最初にこの状況に遭遇したときはびっくりしましたが、冷静に考えるととても妥当な仕組みなのかなと思います。

余談ですが、タイトルにも「油断」、と油が入っていました。
こちらは仏教用語のようですね。

タイトルとURLをコピーしました