株式会社ネットワールドのエンジニアがお届けする技術情報ブログです。
各製品のエキスパートたちが旬なトピックをご紹介します。

AWS WAF - 入れ子構造のユーザー独自ルール作成方法

皆様こんにちは!ネットワールドSEの渡邊です!
前回は検証用にAWS WAFを構築する手順を紹介しました。

 


blogs.networld.co.jp

今回は特定リクエストのみを許可する際に必要となる、ユーザー独自ルールの作成、及び入れ子構造(ネスト構造)のルール作成について解説していこうと思います。

実際に起こりえる可能性のあるシナリオを例にしながら解説していきます。

 

 

背景

AWS WAFでユーザー独自のルールを作成する際は、Rule Visual Editorという機能によって、視覚的に分かりやすくルールを作成することが可能です。

ですが、Rule Visual Editorだけでは、特定条件によるANDの中にOR、ORの中にANDを含む、入れ子構造(ネスト構造)のルールを作成することが出来ません。作成しようと思った場合はRule JSON EditorというJSON表現による編集が必要となります。

docs.aws.amazon.com

とは言え1からJSONでルールを作成するのは手間な上に難易度が高いです。本記事ではRule Visual EditorとRule JSON Editorを併用し、テンプレ構文を紹介しつつ、なるべく手間を抑えて入れ子構造のルールを作成する方法を紹介します。

 

想定シナリオ

本記事では、XSS攻撃もしくはSQLi攻撃の疑いがあるリクエストの内特定のURI Path「/test1/」へのリクエストを許可し、他は全てブロックするという動作を実装してみます。

ユーザーの通信がAWS WAFによりブロックされてしまい、Sampled Requestsで調査したところ、XSS攻撃、SQLi攻撃として誤検知されていたと判明した際のルールチューニングを想定したシナリオです。

 

骨組みとなる簡単なルールを作成

まずはRule Visual Editorを使用して、JSON編集を楽にするため骨組みとなるルールを作成します。

動作としては「URI Path「/test1/」以外へのリクエストで、XSS攻撃被疑のLabelが付与済みであった場合はブロック」となるルールを作成します。

Labelというのは簡単にいうと、Countモードのルールでリクエストに付与したカスタムヘッダーです。Countモードへ変更する手順については後述します。

前提条件として「Default web ACL action for requests that don't match any rules」はAllowに設定する必要があります。ブロックされなかった「/test1/」宛のリクエストはこれで許可することになります。

「Default web ACL action for requests that don't match any rules」というのはどのルールにも引っかからなかったリクエストに対するデフォルトアクションを設定する項目です。デフォルトではAllowです。


ルール作成画面まで

Rules」タブから、「Add rules」を選択し、「Add my own rules and rule groups」を選択します。

ルール作成の画面に遷移します。

骨組みとなるルール作成は「Rule Visual Editor」を利用します。後ほど詳細なルール編集(JSON表現)で「Rule JSON Editor」を使用します。

Name」には任意の名前を入力します。(本記事ではtest-ruleと入力します)

 

条件を整理

論理演算の形にして整理してみると以下のようになります。

  • A … URI Pathが/test1/のリクエストである
  • B … XSS攻撃被疑ラベルが付与済みのリクエストである

つまり、not A・B(not A かつ B)が真であればブロック、とするルールを作成すれば良さそうであることが分かります。

ルールを作成

If a request」の横のプルダウンから「matches all the statements(AND)」を選択します。

 

Statement 1で以下のように選択・入力します。

  • Negate statement resultsにチェック(Statement 1がNOT Statement 1に名前が変わります。)
  • Inspect … URI Pathを選択
  • Match type … Start with stringを選択
  • String to match … /test1/ と入力

Statement 2で以下のように選択・入力します。

  • Inspect … Has a labelを選択
  • Match type … Labelを選択
  • Match key … CrossSiteScripting_URIPathと入力し検索結果に出てきたものを選択。(awswaf:managed:aws:core-rule-set:CrossSiteScripting_URIPath)

 

Action」でBlockを選択し、「Add rule」をクリックします。

Set rule priorityで作成したルールが、「AWS-AWSManagedRulesCommonRuleSet」「AWS-AWSManagedRulesSQLiRuleSet」の後段になるように適宜Move up、Move downで調整し、「Save」をクリック。

 

これで「URI Path「/test1/」以外へのリクエストで、XSS攻撃被疑のLabelが付与済みであった場合はブロック」となるルールが作成できました。

 

作成したルールをJSONで編集

記事冒頭の説明の通り、Rule Visual Editorでは入れ子構造(ネスト構造)のルールが作成できません

そこで、ここからはRule JSON Editorに切り替えて、先ほど作成したルールに「URI Path「/test1/」以外へのリクエストでSQLi攻撃被疑のLabelが付与済みであった場合はブロック」という動作をJSON編集で追加することで、入れ子構造を実現します。

条件を整理

記事が長くなってしまったので、ここで実装したい動作を整理します。

「URI Pathが/test1/」以外のリクエストの内、「XSSまたはSQLi攻撃被疑Labelが付与済み」のリクエストをブロックし、他は通すことが出来れば目的通りの動作となりそうです。

分かりやすくするためにフローチャートの形にするとこんな形です。

 

ではこれを論理演算の形にして整理してみます。上記を以下のように命題に置き換えます。

  • A … URI Pathが/test1/のリクエストである
  • B … XSS攻撃被疑ラベルが付与済みのリクエストである
  • C … SQLi攻撃被疑ラベルが付与済みのリクエストである

これを元として上記のフローチャートを論理演算の形にしてみると以下の論理式になります。

not A・(B+C) … (not Aかつ、BまたはC)

これはANDの中にORが含まれる入れ子構造でありRule Visual Editorでは作成することができないので、ここからはRule JSON Editorに切り替えて編集していきます。

JSON編集画面に行くまでの手順

Rules」タブから先ほど作成したルールを選択します。

 

Edit」をクリック。

 

Rule JSON Editor」をクリック。

作成したルールのJsonを確認すると以下の内容になっています。

ここで覚えておくことは、Statementブロック(Statementsと混同しないように注意)の中にAndStatementやOrStatementブロックが入るということです。

 

{
  "Name": "test-rule",
  "Priority": 7,
  "Action": {
    "Block": {}
  },
  "VisibilityConfig": {
    "SampledRequestsEnabled": true,
    "CloudWatchMetricsEnabled": true,
    "MetricName": "test-rule"
  },
  "Statement": {
    "AndStatement": {
      "Statements": [
        {
          "NotStatement": {
            "Statement": {
              "ByteMatchStatement": {
                "FieldToMatch": {
                  "UriPath": {}
                },
                "PositionalConstraint": "STARTS_WITH",
                "SearchString": "/test1/",
                "TextTransformations": [
                  {
                    "Type": "NONE",
                    "Priority": 0
                  }
                ]
              }
            }
          }
        },
        {
          "LabelMatchStatement": {
            "Scope": "LABEL",
            "Key": "awswaf:managed:aws:core-rule-set:CrossSiteScripting_URIPath"
          }
        }
      ]
    }
  }
}

 

基本構造

ANDとORステートメントのJSONにおける基本構造は以下の通りです。

・AND構造

  "Statement": {
    "AndStatement": {
      "Statements": [
        { 条件1 },
        { 条件2 }
      ]
    }
  }

・OR構造

  "Statement": {
    "OrStatement": {
      "Statements": [
        { 条件1 },
        { 条件2 }
      ]
    }
  }

・NOT構造

NOTにしたい条件を"NotStatement" : { "Statement" : { 条件 } } となるように囲む形です。

    "NotStatement": {
        "Statement": { 条件 }
        }

 

これらを踏まえると、入れ子構造(ネスト構造)を作成する場合は「条件1」「条件2の中にAndStatementやOrStatement構造を書けば良さそうです

具体的には以下のようにJSONを作成すれば良いことになります。

"Statement": {
    "AndStatement": {
        "Statements": [
            {
                "OrStatement" : {
                    "Statements": [
                        { 条件1 },
                        { 条件2 }
                    ]
                }
            },
            {
                "OrStatement" : {
                    "Statements": [
                        { 条件1 },
                        { 条件2 }
                    ]
                }
            }
        ]
    }
}

 

もしORの中にANDを含ませたい場合、上記JSONのAndStatementとOrStatementを入れ替えればOKです。

実際に作成したルール

以上のことを踏まえて実際に作ったルールは以下の通りです。

やったこととしてはまず、前述のJSONのルールの「"LabelMatchStatement"」の部分をそのままOR構造に置き換えました。

そして置き換えたOR構造の中の条件1、条件2にそれぞれXSS、SQLiのLabel付与済みかを判定する「"LabelMatchStatement"」を挿入した形です。

 

{
  "Name": "test-rule",
  "Priority": 7,
  "Action": {
    "Block": {}
  },
  "VisibilityConfig": {
    "SampledRequestsEnabled": true,
    "CloudWatchMetricsEnabled": true,
    "MetricName": "test-rule"
  },
  "Statement": {
    "AndStatement": {
      "Statements": [
        {
          "NotStatement": {
            "Statement": {
              "ByteMatchStatement": {
                "FieldToMatch": {
                  "UriPath": {}
                },
                "PositionalConstraint": "STARTS_WITH",
                "SearchString": "/test1/",
                "TextTransformations": [
                  {
                    "Type": "NONE",
                    "Priority": 0
                  }
                ]
              }
            }
          }
        },
        {
            "OrStatement" : {
                "Statements": [
                    {
                        "LabelMatchStatement": {
                            "Scope": "LABEL",
                            "Key": "awswaf:managed:aws:core-rule-set:CrossSiteScripting_URIPath"
                            }
                    },
                    {
                        "LabelMatchStatement": {
                            "Scope": "LABEL",
                            "Key": "awswaf:managed:aws:sql-database:SQLi_URIPath"
                            }
                    }
                ]
            }
        }
      ]
    }
  }
}

 

この内容をRule JSON Editorで入力し、文法ミスがないか「Validate」ボタンをクリックして確認します。「Rule is valid」と表示されればOKです。

 

 

Save rule」をクリックします。

 

Set rule priorityの画面に遷移するので、ルールの順番が変わっていないことを確認して「Save」をクリックします。

 

 

これで入れ子構造(ネスト構造)のユーザー独自ルールを作成することができました。

ここまでが本記事の趣旨ですが、実際に正常リクエストか否かをこのルールに判別させるためには、誤検知していたルールをCountモードに変更する必要があります。

 

ルールをCountモードに変更する手順

最後に「CrossSiteScripting_URIPATH」「SQLi_URIPATH」のルールをBlockモードからCountモードに変更します。

Countは他WAFでいうトランスペアレントモードに近いですが、違いとしてはLabelと呼ばれるカスタムヘッダーをリクエストに付加します。このLabelをユーザー独自ルールを作成する際にも利用することとなります。

docs.aws.amazon.com

この手順により、今までXSS攻撃、SQLi攻撃と判定された通信は全てBlockしていたのを、先程の手順で作成したユーザ独自ルールで正常リクエストかどうか判別させることが出来るようになります。

XSS攻撃に関するルールをCountモードに変更

まずはルール一覧から該当するルールを選択します。「CrossSiteScripting_URIPATH」は「AWS-AWSManagedRulesCommonRuleSet」の中にあるので、「AWS-AWSManagedRulesCommonRuleSet」を選択します。

右上の「Edit」を選択します。

 

画面下の方にあるCore rule set rulesの中から、CrossSiteScripting_URIPATHのプルダウンから、「Override to Count」を選択して、Save ruleをクリックします。

Set rule priority画面に遷移しますが、特に何も変更せず、Saveをクリック。

本手順によりURI Pathを利用したXSS攻撃の疑いがあるリクエストはCountした上で後段のルールに通されます。

 

SQLi攻撃に関するルールをCountモードに変更

手順自体はXSS攻撃に関するルールをカウントモードに変更するのと同じです。選択するルールが違うだけですので、選択するルールについてのみの説明となります。

AWS-AWSManagedRulesSQLiRuleSet」を選択します。

 

SQLi_URIPATH」を選択してCountモードに変更します。

まとめ

本手順で入れ子構造(ネスト構造)のユーザー独自ルールの作成手順と、JSON表現における基本構造を説明しました。

AWS WAFで細かいチューニングを行う場合、論理演算の知識やJSON構文による編集が必要となることは多いですが、Rule Visual Editorも併用することである程度楽をすることも可能です。

本記事が、入れ子構造のルールが作れず上手く行かない方の助けになれれば幸いです。