電通総研 テックブログ

電通総研が運営する技術ブログ

AWS WAF マネージドルールグループの個別ルールに条件を追加する方法

こんにちは。X(クロス)イノベーション本部 ソフトウェアデザインセンター セキュリティグループの耿です。

AWS WAF には AWS マネージドルールが複数提供されており、利用することで AWS が定めた条件に一致するリクエストをブロックしてくれます。各マネージドルールグループには、複数の個別のルールが含まれています。例えばコアルールセット (CRS) マネージドルールグループには、v1.6 時点で 22 個の個別ルールが含まれています。

コアルールセットのルール一覧

マネージドルールグループの個別ルールには、アプリケーションによっては条件が厳しすぎるものもあります。例えば以下の個別ルールによってリクエストをブロックして欲しくない場合があるかもしれません。

マネージドルール名 検出条件
SizeRestrictions_BODY リクエストボディサイズが 8 KB を超える場合はブロック
EC2MetaDataSSRF_BODY リクエストボディに「localhost」「127.0.0.1」の文字列が含まれている場合はブロック(*注1)
GenericLFI_BODY リクエストボディに「../../」の文字列が含まれている場合はブロック、など

(*注1) 筆者が確認した条件であり、公式のアナウンスではありません。また他にも条件があるかもしれません。

マネージドルールがアプリケーションの要件に合わない時は、個別のルールのアクションを Count にオーバーライドすることができます。

ルールアクションのオーバーライド

しかしこれではあらゆる状況においてそのルールによるブロックが無効化されてしまい、マネージドルールを使っている効果が薄れてしまいます。そうではなく、「特定の条件においてのみ」マネージドルールのアクションを Count に変更したい場合があると思います。こちらでも紹介されているような方法であり、AWS WAF のラベルを利用して実現します。

マネージドルールに条件を追加する仕組み

まず、特定の条件において「のみ」マネージドルールのアクションを変更するための仕組みを説明します。

マネージドルールの判定条件に一致した場合、一致した目印として特定の「ラベル」がリクエストに付与されます。ルールアクションを Count にオーバーライドした場合も、ブロックはされなくなりますが「ラベル」は Block の時と変わらず付与されます。例えば EC2MetaDataSSRF_BODY ルールに一致した場合は awswaf:managed:aws:core-rule-set:EC2MetaDataSSRF_Body というラベルが付与されます。付与されたラベルは Web ACL 内の後続のルールからも確認することができます。

付与されるラベル

マネージドルールグループの後に評価される自作ルールを追加し、Statement に「(Count にオーバーライドした)マネージドルールに一致した場合に付与されるラベル」と「ブロックを有効にしたい条件」を AND 条件で評価させることで、「特定の条件においてのみ」アクションを Count に変更することができます。例えば、リクエストの URI パスが /api/* であれば EC2MetaDataSSRF_BODY ルールによるブロックを無効にしたいが、他の URI パスではそのまま有効にしたい場合、次の2つの Statement を AND 条件で繋げ、アクションを Block とした自作ルールを追加します。

  • リクエストに「awswaf:managed:aws:core-rule-set:EC2MetaDataSSRF_Body」ラベルがある
    かつ
  • URIパスが /api/* ではない (NotStatement)
    場合に、Block

自作ルール

ここからは、これを実際にマネジメントコンソールで試してみます。

(準備)ALBの作成

WAF Web ACL を関連付けるための ALB を作成し、固定レスポンスを返すようにします。

ALBからの固定レスポンス1

WAF Web ACL 作成

WAF Web ACL を作成し、ALB に関連付けます。

「Create web ACL」をクリック
WAFの作成1

Web ACL の名前を入力し、前のステップで作成した ALB を関連付け、「Next」をクリック
WAFの作成2

「Add managed rule groups」をクリック
WAFの作成3

AWS managed rule groups」にある「Core rule set」のスイッチをオン
WAFの作成4

あとはデフォルト設定のまま Web ACL の作成を完了させます。

WAFの動作確認①:マネージドルールの動作確認

WAF Web ACL が関連付けられた状態の ALB にリクエストを送ってみます。
ボディに何も含まれていない POST リクエストは、ブロックされることなく ALB に設定した固定レスポンスが返ってきます。

ALBからのレスポンス2

しかしリクエストボディに「localhost」を含めると、WAF にブロックされ 403 が返されます。
ALBからのレスポンス3

個別ルールのアクションのオーバーライド

EC2MetaDataSSRF_BODY ルールのアクションを Count にオーバーライドし、リクエストボディに「localhost」があってもブロックされないようにしていきます。

先ほど作成した WAF Web ACL に追加したマネージドルールをチェックし、「Edit」を選択
WAFの作成5

EC2MetaDataSSRF_BODY ルールアクションを Override to Count に変更し、「Save rule」をクリック
WAFの作成6

次の画面でも「Save」をクリック
WAFの作成7

WAFの動作確認②:マネージドルール無効化の確認

再びリクエストボディに「localhost」を含めるリクエストを送信すると、今度はブロックされずに固定レスポンスが返ってきます。

ALBからのレスポンス4

現在、あらゆる状況において EC2MetaDataSSRF_BODY ルールによるブロックが無効化されている状態です。次のステップからは「URI パスが /api/ から始まる場合に限り、ブロックを無効化する」ようにカスタマイズしていきます。

自作ルールの追加

「Add my own rules and rule groups」をクリック
WAFの作成8

ビジュアルエディタで次のように Statement を組み立て、「Add rule」をクリック
WAFの作成9
WAFの作成10
WAFの作成11

優先順位がマネージドルールグループよりも後ろであることを確認し、「Save」をクリック
WAFの作成12

WAFの動作確認③:自作ルールの確認

再びリクエストボディに「localhost」を含めるリクエストを送信すると、今度はブロックされました。
ALBからのレスポンス5

しかし、リクエスURI を「/api/hello」とすると、ブロックされずに通過しました。
ALBからのレスポンス6

以上で、マネージドルールグループの個別ルールに条件を追加することができました。

より複雑な条件の場合

以上はシンプルな条件について確認しましたが、条件が複雑な場合の自作ルールの組み方を簡単に説明します。例として以下の場合です。

  • マネージドルールグループの EC2MetaDataSSRF_BODY ルールと GenericLFI_BODY ルールを対象にしたい
  • リクエストがこれらのルールに一致した場合、「リクエスURI/api/*」かつ「content-type ヘッダーが application/json」の場合はブロックされないようにしたい。それ以外の場合はブロックしたい

このような自作ルールは次のように実現できます。
自作ルール2

今のところ AND 条件や OR 条件が多重になるルールはビジュアルエディタで作成できませんので、JSON エディタに切り替えてルールを編集することになります。

WAFの作成13

例に示した複雑な条件の自作ルールは、以下のような JSON で作れます。

{
  "Name": "custom-body-block",
  "Priority": 1,
  "Statement": {
    "AndStatement": {
      "Statements": [
        {
          "OrStatement": {
            "Statements": [
              {
                "LabelMatchStatement": {
                  "Scope": "LABEL",
                  "Key": "awswaf:managed:aws:core-rule-set:EC2MetaDataSSRF_Body"
                }
              },
              {
                "LabelMatchStatement": {
                  "Scope": "LABEL",
                  "Key": "awswaf:managed:aws:core-rule-set:GenericLFI_Body"
                }
              }
            ]
          }
        },
        {
          "NotStatement": {
            "Statement": {
              "AndStatement": {
                "Statements": [
                  {
                    "ByteMatchStatement": {
                      "SearchString": "/api/",
                      "FieldToMatch": {
                        "UriPath": {}
                      },
                      "TextTransformations": [
                        {
                          "Priority": 0,
                          "Type": "NONE"
                        }
                      ],
                      "PositionalConstraint": "STARTS_WITH"
                    }
                  },
                  {
                    "ByteMatchStatement": {
                      "SearchString": "application/json",
                      "FieldToMatch": {
                        "SingleHeader": {
                          "Name": "content-type"
                        }
                      },
                      "TextTransformations": [
                        {
                          "Priority": 0,
                          "Type": "NONE"
                        }
                      ],
                      "PositionalConstraint": "EXACTLY"
                    }
                  }
                ]
              }
            }
          }
        }
      ]
    }
  },
  "Action": {
    "Block": {}
  },
  "VisibilityConfig": {
    "SampledRequestsEnabled": true,
    "CloudWatchMetricsEnabled": true,
    "MetricName": "custom-body-block"
  }
}

さいごに

AWS WAF マネージドルールグループの個別ルールの適用条件を、ラベルと自作ルールを使ってカスタマイズする方法を試しました。最初は難しそうに見えるかもしれませんが、理屈を理解すると柔軟にマネージドルールをカスタマイズできるようになるので、AWS WAF をより使いこなしたい方はぜひ試してみてください。


私たちは同じチームで働いてくれる仲間を大募集しています!たくさんのご応募をお待ちしています。

セキュリティエンジニア

執筆:@kou.kinyo、レビュー:@wakamoto.ryosuke
Shodoで執筆されました