[AWS WAF] マネージドルールグループに対して例外条件を設定する

AWS WAFを使いこなすポイントは「ラベル」にあった?!
2022.08.25

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

みなさん、こんにちは!
福岡オフィスの青柳です。

AWS WAFの「マネージドルールグループ」は、AWSやサードパーティから提供される様々なシチュエーションに応じたWAFルールのセットです。

マネージドルールグループを使うことで、細かな設定を行うことなくAWSや各セキュリティベンダーのノウハウに従ったセキュリティ対策を行うことができ、また、ルール内容の自動更新によって最新の脅威や脆弱性に対応することができます。

このように便利で運用の手間も少なくて済むマネージドルールグループですが、要件によっては「特定の条件の場合はルールを適用除外したい」という場合もあるかと思います。

  • 特定のIPアドレスからのアクセス
  • 特定のURIパスへのアクセス
  • 特定のヘッダを持つリクエスト
  • etc.

今回は、そのような場合の対応方法について解説します。

事前準備

AWS WAFにマネージドルールグループを設定する

対応方法について解説する前に、まずは前提となるマネージドルールグループの設定をAWS WAFに対して行いたいと思います。

今回はサンプルとして「Application Load Balancer (ALB)」に対してAWS WAFを適用する構成とします。
ALBの背後にはEC2インスタンスを配置してWebサーバーを実行します。(ALBやWebサーバーの設定手順については説明を割愛します)

まず、マネジメントコンソールのAWS WAF画面から「Web ACLs」を選択して、対象リージョンを「Asia Pacific (Tokyo)」に変更します。(適用対象がALBであるため)

「Create web ACL」をクリックしてWeb ACLの作成を開始します。

名前などの情報を入力します。
リソースタイプは「リージョナル」を選択して、リージョンが「Asia Pacific (Tokyo)」になっていることを確認します。

対象となるAWSリソース (ここではALB) を指定します。(これはWeb ACLの作成後に行っても構いません)

「Add rules」→「Add managed rule groups」の順に選択します。

マネージドルールグループを追加する画面になりますので、下の方にスクロールして・・・

「AWS managed rule groups」→「Free rule groups」の一覧から「Core rule set」を見つけます。

「Add to web ACL」のスイッチを「オン」に変更します。

一番下の「Add rules」をクリックして追加を完了します。

元の画面に戻りますので、ルールの一覧に「AWS-AWSManagedRulesCommonRuleSet」が登録されていることを確認します。

デフォルトアクションは「Allow」を選択します。

ルールのプライオリティは、そのまま変更せず「Next」をクリックします。

メトリクスの設定はデフォルトのままで構いません。

サンプリングオプションは「Enable sampled requests」を選択します。(AWS WAFの動作結果を確認するために、ログ代わりにサンプリング結果を参照します)

以上の設定内容でWeb ACLを作成します。

AWS WAFの動作を確認する

それでは、この状態でAWS WAFの動作を確認しましょう。

まず、Webブラウザを使って普通にアクセスしてみます。
ALBのDNS名を指定してアクセスします。

正常にアクセスが行えました。

次に、AWS WAFで検知・遮断されるアクセスを行ってみます。

今回設定したマネージドルールグループ「AWSManagedRulesCommonRuleSet」に含まれるルールの1つに「NoUserAgent_HEADER」というものがあります。
これは、HTTPリクエストのヘッダ「User-Agent」に値が設定されていない場合にアクセスを拒否するルールです。

curlコマンドを使って、このルールに検知されるようなアクセスを行ってみます。
(-HオプションでヘッダUser-Agentを指定していますが、設定値を空文字としています)

$ curl -i -H "User-Agent:" http://example-elb-XXXXXXXXXX.ap-northeast-1.elb.amazonaws.com

以下のような結果が返ってくるはずです。

HTTP/1.1 403 Forbidden
Server: awselb/2.0
Date: Mon, 22 Aug 2022 10:10:07 GMT
Content-Type: text/html
Content-Length: 118
Connection: keep-alive

<html>
<head><title>403 Forbidden</title></head>
<body>
<center><h1>403 Forbidden</h1></center>
</body>
</html>

「403 Forbidden」が返ってきたので、アクセスが拒否されたことが判ります。

アクセスログ (AWS WAFのSampled requests) を確認してみましょう。

1行目は、Webブラウザでアクセスした時のログであり、アクセスが許可されたことを示しています。

2行目は、curlコマンドでアクセスした時のログであり、ルール「NoUserAgent_HEADER」に検知された結果、アクセスが拒否されたことを示しています。

前提となるAWS WAFのマネージドルールグループの設定を行い、動作が確認できました。
次は、今回の本題である「例外条件」の設定方法について説明します。

例外条件を設定する

マネージドルールグループに対して例外条件を設定するやり方として、以下の2つのパターンがあります。

  • パターン(1): マネージドルールグループ全体へ例外条件を適用する
  • パターン(2): マネージドルールグループの一部のルールに対して例外条件を適用する

それぞれの手順について順に説明します。

パターン(1): マネージドルールグループ全体へ例外条件を適用する

マネージドルールグループ全体に対して「例外条件」を設定するには、Web ACLルール設定の「スコープダウンステートメント」を使います。

例として、「アクセス対象のURIパスが/api/で始まる場合は、マネージドルールグループの検知対象から除外する」という設定を行ってみます。

まず、設定の対象となるマネージドルールグループにチェックを入れて「Edit」をクリックします。

ルールの編集画面が表示されるので、下の方にスクロールします。

「Enable scope-down statement」のチェックボックスがありますので、チェックを入れます。

ステートメントの入力が可能になりましたので、以下のように設定します。

  • If a request: 「doesn't match the statement (NOT)」を選択
  • Statement:
    • Inspect: 「URI Path」を選択
    • Match type: 「Start with string」を選択
    • String to match: 「/api/」と入力
    • Text transformation: 「None」を選択

最後に「Save rule」をクリックして、設定完了です。

動作確認

例外条件を設定する前の動作確認と同様に、ルール「NoUserAgent_HEADER」に検知されるアクセスを試して動作を確認してみましょう。

まずは、URIパス/に対するアクセスを行ってみます。

$ curl -i -H "User-Agent:" http://example-elb-XXXXXXXXXX.ap-northeast-1.elb.amazonaws.com
HTTP/1.1 403 Forbidden
Server: awselb/2.0
Date: Mon, 22 Aug 2022 10:10:07 GMT
Content-Type: text/html
Content-Length: 118
Connection: keep-alive

<html>
<head><title>403 Forbidden</title></head>
<body>
<center><h1>403 Forbidden</h1></center>
</body>
</html>

さきほどと同じくアクセスが拒否されました。

では、次にURIパス/api/に対するアクセスを行ってみます。

$ curl -i -H "User-Agent:" http://example-elb-XXXXXXXXXX.ap-northeast-1.elb.amazonaws.com/api/
HTTP/1.1 200 OK
Date: Mon, 22 Aug 2022 12:14:18 GMT
Content-Type: text/html; charset=UTF-8
Content-Length: 3
Connection: keep-alive
Server: Apache/2.4.54 ()
Upgrade: h2,h2c
Last-Modified: Mon, 22 Aug 2022 09:54:00 GMT
ETag: "3-5e6d16d5122aa"
Accept-Ranges: bytes

OK

ヘッダ「User-Agent」に値を設定していないのにもかかわらず、アクセスが許可されました。

つまり、設定した「例外条件」が正しく動作したということが判りました。

パターン(2): マネージドルールグループの一部のルールに対して例外条件を適用する

「スコープダウンステートメント」を使うことでマネージドルールグループに対して例外条件を適用できることが分かりました。
しかし、この方法ではマネージドルールグループ内の全てのルールに対して一律で例外条件が適用されてしまいます。
「特定のルールのみに例外条件を適用したい」という場合には、こちらの「パターン(2)」の方法を使います。

パターン(2)の設定方法は、パターン(1)の設定方法よりも複雑であるため、まずは方式の概要を図で示します。

まず、マネージドルールグループの中で例外条件を適用したいルールについて、検知時のアクションを「BLOCK」→「COUNT」へ変更します。・・・(1)

COUNTモードのルールで検知されたアクセスは、通信は遮断されませんが、検知されたことを表す「ラベル」が付与されます。・・・(2)

付与されるラベルは以下のような命名規則となっています:
awswaf:managed:<ベンダー名>:<マネージドルールグループ名>:<ルール名>

例えば、今回の場合はラベルは以下のようになります:
awswaf:managed:aws:core-rule-set:NoUserAgent_Header

これだけですとCOUNTモードのルールで検知されたアクセスは全て許可されてしまうため、ラベルが付与されたアクセスに対して検知を行うカスタムルールを作成します。・・・(3)

このルールに設定する検知条件は、以下のような2つの条件を「AND」を使って組み合わせたものにする必要があります:

  • 条件1: ラベルawswaf:managed:aws:core-rule-set:NoUserAgent_Headerが付与されている
  • 条件2: 対象URIパスが/api/で始まるもの以外 (NOT条件)

COUNTモードのルールで検知されてラベルが付与されたアクセスは、この検知条件によって以下のように挙動が決まります。

  • 対象URIパスが/api/で始まるもの   → ブロックされない ・・・(5)
  • 対象URIパスが/api/で始まるもの以外 → ブロックされる  ・・・(4)

このようにして、例外条件を適用しないルールに対しては通常通りの検知の判定が行われ、例外条件を適用したルールに対してはカスタムルールによる検知の判定が行われるという訳です。

設定を行う

では、上の図の内容に従って実際の設定を行っていきます。

まず、設定の対象となるマネージドルールグループにチェックを入れて「Edit」をクリックします。

ルールの編集画面が表示されるので、例外条件を適用したルール (今回の場合は「NoUserAgent_HEADER」) について、アクションの「Count」をオンにします。

なお、この時、パターン(1)で「スコープダウンステートメント」を設定した場合は、設定を取り消して元の状態に戻しておいてください。

「Save rule」をクリックして設定を保存しましたら、ルール一覧の画面に戻ります。

続いて、「Add rules」→「Add my own rules and rule groups」の順に選択して、カスタムルールを作成していきます。

以下のように設定します。

  • Rule type: 「Rule builder」を選択
  • Rule:
    • Name: 任意の名前を入力
    • Type: 「Regular rule」を選択

「If a request」の選択肢から「matches all the statements (AND)」を選択します。

1つ目の条件を以下のように設定します。

  • Statement 1:
    • Negate statement (NOT): チェックしない
    • Inspect: 「Has a label」を選択
    • Labels:
      • Match scope: 「Label」を選択
      • Match key: 「awswaf:managed:aws:core-rule-set:NoUserAgent_Header」を選択

2つ目の条件を以下のように設定します。

  • Statement 2:
    • Negate statement (NOT): チェックする
    • Inspect: 「URI Path」を選択
    • Match type: 「Start with string」を選択
    • String to match: 「/api/」と入力
    • Text transformation: 「None」を選択

アクションの選択肢から「Block」を選択します。

「Add rule」をクリックして設定を保存します。

プライオリティの設定では、「マネージドルールグループ」よりも「カスタムルール」を低い優先度に設定してください。(先にマネージドルールグループが評価される必要があるためです)

「Save」をクリックして設定を保存します。

これで全ての設定は終わりです。

動作確認

最初に、例外条件を適用したルール「NoUserAgent_HEADER」の動作を確認してみます。

まず、URIパス/に対するアクセスを行います。

$ curl -i -H "User-Agent:" http://example-elb-XXXXXXXXXX.ap-northeast-1.elb.amazonaws.com
HTTP/1.1 403 Forbidden
Server: awselb/2.0
Date: Mon, 22 Aug 2022 10:10:07 GMT
Content-Type: text/html
Content-Length: 118
Connection: keep-alive

<html>
<head><title>403 Forbidden</title></head>
<body>
<center><h1>403 Forbidden</h1></center>
</body>
</html>

例外条件に合致しないため、アクセスが拒否されました。

アクセスログは以下のようになっています。

1行目は、アクセスがマネージドルールグループのルール「NoUserAgent_HEADER」で検知されて「COUNT」アクションが行われたことを示しています。

2行目は、ラベルが付与されたアクセスがカスタムルールで評価された結果、検知対象となって「BLOCK」されたことを示します。

今度は、URIパス/api/に対するアクセスを行います。

$ curl -i -H "User-Agent:" http://example-elb-XXXXXXXXXX.ap-northeast-1.elb.amazonaws.com/api/
HTTP/1.1 200 OK
Date: Mon, 22 Aug 2022 12:14:18 GMT
Content-Type: text/html; charset=UTF-8
Content-Length: 3
Connection: keep-alive
Server: Apache/2.4.54 ()
Upgrade: h2,h2c
Last-Modified: Mon, 22 Aug 2022 09:54:00 GMT
ETag: "3-5e6d16d5122aa"
Accept-Ranges: bytes

OK

例外条件に合致したため、アクセスが許可されました。

アクセスログは以下のようになっています。

1行目は、さきほどと同じく、アクセスがマネージドルールグループのルール「NoUserAgent_HEADER」で検知されて「COUNT」アクションが行われたことを示しています。

2行目は、Web ACL「example-web-acl」の最終的な評価結果として「ALLOW」となったことを示しています。
これは、ラベルが付与されたアクセスがカスタムルールで評価された結果、検知対象とならなかったことを意味しています。(Sampled requestsの仕様で、ルールで検知対象とならなかったものはログに残らないため、最終結果で判断することになります)

以上の結果から、期待通りに例外条件が適用された動作が確認できました。

では次に、例外条件を適用していないルールについて動作を確認します。

ここではマネージドルールグループ「AWSManagedRulesCommonRuleSet」に含まれるルール「UserAgent_BadBots_HEADER」について確認します。

このルールは、攻撃者が使うことの多いボットツールによるアクセスを検知するものです。
AWSドキュメントによると、検知対象のツールの一つに「Nmap」というツールが挙げられているので、今回はこちらを使います。

※ このツール自体が有害であるとか脆弱性があるということではありません。あくまで攻撃者が攻撃対象のセキュリティ脆弱性を調べる際に使う場合があるということです。

このルールは、アクセス内容からツールを判定する訳ではなく単に「User-Agent」ヘッダをチェックしているだけですので、以下のようにcurlコマンドを実行してみます。

$ curl -i -H "User-Agent: Mozilla/5.0 (compatible; Nmap Scripting Engine; http://nmap.org/book/nse.html" http://example-elb-XXXXXXXXXX.ap-northeast-1.elb.amazonaws.com

以下のような結果が返ってきました。

HTTP/1.1 403 Forbidden
Server: awselb/2.0
Date: Mon, 22 Aug 2022 12:15:50 GMT
Content-Type: text/html
Content-Length: 118
Connection: keep-alive

<html>
<head><title>403 Forbidden</title></head>
<body>
<center><h1>403 Forbidden</h1></center>
</body>
</html>

アクセスが拒否されたことが判ります。

アクセスログは以下のようになっています。

ルール「UserAgent_BadBots_HEADER」によってブロックされたことが確認できました。

では次に、同様に「User-Agent」ヘッダにNmapツールの情報を設定しつつ、URIパスを/api/と指定してアクセスしてみましょう。

$ curl -i -H "User-Agent: Mozilla/5.0 (compatible; Nmap Scripting Engine; http://nmap.org/book/nse.html" http://example-elb-XXXXXXXXXX.ap-northeast-1.elb.amazonaws.com/api/

結果は以下のようになりました。

HTTP/1.1 403 Forbidden
Server: awselb/2.0
Date: Mon, 22 Aug 2022 12:15:50 GMT
Content-Type: text/html
Content-Length: 118
Connection: keep-alive

<html>
<head><title>403 Forbidden</title></head>
<body>
<center><h1>403 Forbidden</h1></center>
</body>
</html>

この場合も、アクセスが拒否されました。

アクセスログは以下の通りです。

こちらも、ルール「UserAgent_BadBots_HEADER」によってブロックされたことが確認できました。

これらの結果から、例外条件を適用していないルール「UserAgent_BadBots_HEADER」では、ルール「NoUserAgent_HEADER」向けに設定した例外条件は適用されていないことが確認できました。

おわりに

AWS WAFのマネージドルールグループを利用する際に「特定の条件の場合はルールを適用除外する」方法について、2つのパターンをご紹介しました。

これまで、AWS WAFのマネージドルールグループを使う場面でアクセスが意図せずブロックされてしまう場合、やむなくルール自体を適用対象から外してしまっていた方もいらっしゃるかもしれません。
そうすると、本来はブロックすべきアクセスも許可してしまうことになり「モヤモヤ」としていたのではないでしょうか。

このような場合に「パターン(2): マネージドルールグループの一部のルールに対して例外条件を適用する」を使うと、ルール毎に個別に例外条件を設定することができます。
是非お試しください。