AWSのWAFでブロックされた正規アクセスを許可する

# 概要
開発中のwebサービスにコメント投稿機能があるのですが、コメント内にURLを含むとコメントが出来ないことが分かりました。
調べてみるとAWSのWAFでブロックされていることが分かったので、許可ルールとして登録することでコメントできるように対応しました。

# 環境
AWSでECSを使ってアプリをデプロイしており、ELBで外部からのアクセスを受けています。
ELBにWAFをアタッチしており、不正アクセスをブロックしているという環境です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252317/90b570d2-c791-afcb-2492-786f4d51527d.png)

# 発生事象
コメント内にURLを含むとコメントすると403エラーとなっており、ELBののログを見るとWAFにブロックされていました。
さらにWAFのサンプルリクエスト機能からコメント投稿に関するアクセスを検索してみると、WAFに設定したブロックのルールに該当していました。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252317/ace73747-755c-7636-e9ec-8bb1887a775c.png" width=70%>

対象のルールは [Fortinet Managed Rules for AWS WAF - Complete OWASP Top 10](https://aws.amazon.com/marketplace/pp/prodview-p77unujkxrg7g)を適用したもので、URL付きのコメントをクロスサイトスクリプティングとみなしてブロックしていました。

アプリ側で改修する方法もありますが、一旦WAFで許可することにします。

# 対応内容
WAFはterraformで管理しているので、terraformで `aws_wafv2_regex_pattern_set`と `aws_wafv2_rule_group`リソースを新たに作成し、`aws_wafv2_web_acl`の中に許可ルールを追加することでコメント投稿のURLからのみ投稿できるようにしました。

* aws_wafv2_regex_pattern_set
コメント用のURLを正規表現で登録します。
```
resource "aws_wafv2_regex_pattern_set" "this" {
  name = aws_wafv2_regex_pattern_set
  scope = "REGIONAL"

  regular_expression {
    regex_string = "foo/bar/ID[A-Z0-9]*/comments" ## コメントのURLを正規表現で登録
  }
}

```

* aws_wafv2_rule_group
`aws_wafv2_regex_pattern_set`のURLかつHTTPのメソッドが`(POST|PUT|PATCH)`のものを許可するルールグループを作成します。

```
resource "aws_wafv2_rule_group" "this" {
  name     = aws_wafv2_rule_group
  scope    = "REGIONAL"
  capacity = 50

  rule {
    name     = "allow_requests"
    priority = 4

    action {
      allow {}
    }

    statement {
      and_statement {
        statement {
          regex_pattern_set_reference_statement {
            arn = aws_wafv2_regex_pattern_set.this.arn

            field_to_match {
              uri_path {}
            }

            text_transformation {
              priority = 0
              type     = "NONE"
            }
          }
        }
        statement {
          or_statement {
            statement {
              byte_match_statement {
                field_to_match {
                  method {}   
                }
                search_string = "POST"
                positional_constraint = "EXACTLY"

                text_transformation {
                  priority = 0
                  type     = "NONE"
                }
              }
            }
            statement {
              byte_match_statement {
                field_to_match {
                  method {}   
                }
                search_string = "PUT"
                positional_constraint = "EXACTLY"

                text_transformation {
                  priority = 0
                  type     = "NONE"
                }
              }
            }
            statement {
              byte_match_statement {
                field_to_match {
                  method {}   
                }
                search_string = "PATCH"
                positional_constraint = "EXACTLY"

                text_transformation {
                  priority = 0
                  type     = "NONE"
                }
              }
            }
          }
        }
      }
    }
    visibility_config {
      cloudwatch_metrics_enabled = true
      metric_name                = "allow_requests"
      sampled_requests_enabled   = true
    }
  }
  visibility_config {
      cloudwatch_metrics_enabled = true
      metric_name                = "allow_requests"
      sampled_requests_enabled   = true
  }
}

```

* aws_wafv2_web_acl
Fortinetのルールでブロックされていたので、Fortinetの手前のpriorityとしてルールを追加します。
```
resource "aws_wafv2_web_acl" "this" {
  name  = aws_wafv2_web_acl
  scope = "REGIONAL"

  default_action {
    allow {}
  }

  rule {
    name     = "AWS-managed_rules_amazon_ip_reputation_list"
    priority = 1

    override_action {
      none {}
    }

    statement {
      managed_rule_group_statement {
        name        = "AWSManagedRulesAmazonIpReputationList"
        vendor_name = "AWS"
      }
    }

    visibility_config {
      cloudwatch_metrics_enabled = true
      metric_name                = "AWS-managed_rules_amazon_ip_reputation_list"
      sampled_requests_enabled   = true
    }
  }

　　　　# 追加ルール
  rule {
    name     = "allow_requests"
    priority = 2

    override_action {
      none {}
    }

    statement {
      rule_group_reference_statement {
        arn = aws_wafv2_rule_group.this.arn
      }
    }
    visibility_config {
      cloudwatch_metrics_enabled = true
      metric_name                = "allow_requests"
      sampled_requests_enabled   = true
    }
  }

  rule {
    name     = "Fortinet-all_rules"
    priority = 3
    override_action {
      none {}
    }

    statement {
      managed_rule_group_statement {
        name        = "all_rules"
        vendor_name = "Fortinet"
      }
    }

    visibility_config {
      cloudwatch_metrics_enabled = true
      metric_name                = "Fortinet-all_rules"
      sampled_requests_enabled   = true
    }
  }

  visibility_config {
    cloudwatch_metrics_enabled = true
    metric_name                = "WebACL"
    sampled_requests_enabled   = true
  }
}
```

# 対応結果
コメントを投稿してみます。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252317/0e31cc04-d338-a3e8-2ae1-272eb4348ebf.png" width=50%>


WAFのサンプルリクエスト機能から追加したルールでフィルタすると、コメント投稿のアクションが`ALLOW`となっていることが確認できました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252317/8cdac4ed-91b2-c4fc-5daa-0480e7ea65c4.png)


# 参考
https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-wafv2-webacl-fieldtomatch.html