AWS WAF v2を利用した共有ヘッダーでのCloudFrontアクセス制限をしてみた

2020.04.27

CloudFront Distributionへのアクセス制限をしたい場合にAWS WAFは非常に有用な手段になります. SQLiなどのセキュリティへの対策のみならずAWS WAF側で処理できる内容であれば, 例えばIP制限や共有ヘッダーでのアクセス制限など柔軟なリクエスト制御を実現することができます. 今回はCloudFront Distributionへのアクセス元を制御するという要件のもと, AWS WAFで共有ヘッダーを持っている場合のみアクセスを許可するという設定をしたいと思います.

なお, S3をOriginとしたCloudFront Distributionの設定が完了した状態から作業を開始していきます.

Web ACL の作成

CloudFront Distributionに紐づけるWeb ACLを作成します. CloudFrontで利用するACLなので, リージョンを「us-east-1」にすることと, scopeを「CLOUDFRONT」に注意しましょう. ルールの中身はByteMatchStatementを利用して「cf-shared-key」の値が「secretsharedkey」の場合にアクセスを許可するようにします. また共有ヘッダーがない場合はアクセスを拒否するので, 「default-action」は「Block={}」とします.

共有ヘッダーについては検証のため緩くしているので, 実際に利用する場合はよく考慮してください.

$ aws wafv2 --region us-east-1 create-web-acl \
  --name eval \
	--scope CLOUDFRONT \
  --default-action Block={} \
  --visibility-config \
    SampledRequestsEnabled=false,CloudWatchMetricsEnabled=false,MetricName=basic-rule \
  --rules file://rule.json

	{
	    "Summary": {
	        "Name": "eval",
	        "Id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxx",
	        "Description": "",
	        "LockToken": "xxxxxxxx-xxxx-xxxx-xxx-xxxxxxxx",
	        "ARN": "arn:aws:wafv2:us-east-1:123456789012:global/webacl/eval/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxx"
	    }
	}

レスポンスに含まれるARNはCloudFrontの設定で利用するので控えておきましょう. ルールは文字列で書くと非常に労力がかかるので, ファイルに記載して入力させます.

rule.json

[
	{
    "Name": "eval",
    "Priority": 1,
    "Statement": {
      "ByteMatchStatement": {
        "SearchString": "secretsharedkey",
        "FieldToMatch": {
          "SingleHeader": {
            "Name": "cf-shared-key"
          }
        },
        "TextTransformations": [{
          "Type": "NONE",
          "Priority": 0
        }],
        "PositionalConstraint": "EXACTLY"
      }
    },
    "Action":{
      "Allow":{

      }
    },
    "VisibilityConfig": {
      "SampledRequestsEnabled":false,
      "CloudWatchMetricsEnabled":false,
      "MetricName": "basic-rule"
    }
	}
]

ここまでで, アクセス制限に利用するWeb ACLを作成しました. この設定だけではCloudFront Distributionへのアクセス制限ができないので, さらに設定していきます.

CloudFront Distributionの更新

Web ACLができたので, CloudFront Distributionに紐付けます. まずはCloudFront Distribution Configを取得して整形します. 必要になるのは, ETagとファイルに出力した, DistributionConfigになります. ETagについては標準出力から控えて作成したconfig.jsonにある, WebACLIdの値として先ほど取得したWeb ACLのARNを入力します. AWS WAF Classicの場合はIDを入力するのですが, 今回はAWS WAF v2を利用しているのでIDではなく, ARNを入力します.

$ aws cloudfront ap-northeast-1 get-distribution-config \
  --id XXXXXXXXXXXXX \
	| tee -a /dev/stderr | jq .DistributionConfig > config.json
  > config.json

{
    "ETag": "YYYYYYYYYYYYY",
    "DistributionConfig": {
    ~~~
    }
}

config.jsonが出来上がったら, 下記コマンドでCloudFront DistributionにWeb ACLを紐付けます. この時, if-matchの項目に先ほど取得したETagを入力します. この結果でエラーなくレスポンスが返ってきたら設定は完了です.

$ aws cloudfront update-distribution \
  --id XXXXXXXXXXXXX \
  --if-match YYYYYYYYYYYYY \
  --distribution-config file://config.json

アクセス制限を試してみる

最後に実際にアクセスしてみて挙動を確認します. アクセスする際のクライアントとしてREST Clientを利用して記載していますが, お好みのクライアントやブラウザで検証して問題ありません.

まずは, 何もヘッダーをつけないでアクセスした場合です. 問題なくアクセスを拒否されています.

###
https://xxxxxxxxxxxxxx.cloudfront.net/

HTTP/1.1 403 Forbidden
Server: CloudFront
Date: Mon, 27 Apr 2020 10:05:51 GMT
Content-Type: text/html
Content-Length: 919
Connection: close
X-Cache: Error from cloudfront
Via: 1.1 xxxxxxxxxxxxxx.cloudfront.net (CloudFront)

次にヘッダーを指定してアクセスを行います. 問題なくアクセスできていますね.

###
https://xxxxxxxxxxxxxx.cloudfront.net/

HTTP/1.1 200 OK
Content-Type: text/html
Transfer-Encoding: chunked
Connection: close
Date: Mon, 27 Apr 2020 08:48:05 GMT
Last-Modified: Wed, 12 Feb 2020 02:56:11 GMT
Server: AmazonS3
Content-Encoding: gzip
Vary: Accept-Encoding
X-Cache: Hit from cloudfront
Via: 1.1 xxxxxxxxxxxxxx.cloudfront.net (CloudFront

さいごに

AWS WAF v2はAWS Managed Rulesで簡単に強力なWAFとして利用できる上に, CloudFrontやALBに対するアクセス制御を柔軟に行うことができます. みなさまのお役にたてれば何よりです.

参考資料