AWS WAFの活用
目次
- 導入方法
- 設定ルールについて
- 接続元IP / 国の制御方法
- ログの保管先
- ログの読み方
導入方法
AWS WAFの導入は、ステップとしては大きく2段階になります。
1.保護パックの作成
2.保護パックへのリソース割り当て
です。
保護パックの作成
保護パックとは、簡単に言うと適用するWAFのルールをまとめたセットです。
どの国からのアクセスを許可する、どんな攻撃パターンを検知したらブロックする、といったルールを設定します。
「WAF & Shield」のページを開きます。

「保護パックを作成」というボタンを押します。

ここで保護対象のリソースを選択して作成することができます。

設定するルールについて、推奨セットも用意されており、ここから選択することもできます。
※保護パックはリソースを紐づけていなくても存在するだけで課金が発生します。作成前に料金(https://aws.amazon.com/jp/waf/pricing/)を確認しておきましょう。

保護パックへのリソース割り当て
リソースの割り当てには大きく3つの方法があります。
1.作成時の「保護するリソース」を選択する方法。こちらは上記の画面から設定する場合です。
2.保護対象のリソースの「統合」タブから保護パックを選択する方法。以下の画像はロードバランサーの場合です。「WAFリソースを関連付ける」というボタンから設定できます。

3.作成した保護パックの編集画面からリソースを選択する方法。「リソースを管理」から設定ができます。

どの方法で設定しても違いはありません。
1つの保護パックに対してリソースは好きなだけ設定できます。保護パックは存在するだけで固定費がかかるため、基本的には1つの保護パックに対して多くのリソースに紐づけておいた方がお得です。(※ただしリクエスト量に応じた従量課金もあるので注意)
設定ルールについて
上記で述べた通り、推奨セットは用意されていますが、もちろん自分でルールを選択して構築することもできます。
ルールにはいくつか種類があります。
- 「AWSマネージドルールグループ」は、AWSが提供している基本的なルールセットです。有料のものもありますが、基本的なところは無料です。
- 「AWS Marketplace ルールグループ」は、有料のルールセットです。使用のためには別途お金がかかります。
- 「カスタムルール」は、自前でルールを設定することができます。特定の国やIPの許可にはこちらが必要になります。自分で作るので無料です。
- 「カスタムルールグループ」は、自分でルールセットを作れる機能です。例えば保護パックを複数作るといった場合に、カスタムルールグループを作成しておけば同じルールを簡単に使いまわせます。

基本的にはまず「AWSマネージドルールグループ」から必要なルールを選択し、その上で追加で欲しいルールを「カスタムルール」で作成する、という流れになるかと思います。
以下が「AWSマネージドルールグループ」です。キャパシティと書いてあるものは簡単に言うとルールコストです。この合計が1500を超えると追加料金が必要になります。1500に収まるように調整しましょう。

各ルールの詳細は公式サイト(https://docs.aws.amazon.com/ja_jp/waf/latest/developerguide/aws-managed-rule-groups-list.html)をご確認ください。
また、上記はルールグループです。グループということはつまり、この中には細かいルールが色々とセットになっています。
例えば、「Anonymous IP list」の中には「AnonymousIPList」と「HostingProviderIPList」という2つのルールが含有されており、ルール単位でアクションの設定が可能です。
ルールに設定できるアクション
ここで言うアクションとはつまり、そのルールに合致した場合のアクセスをどう扱うか?です。
設定できるアクションは以下の5つです。
- Block:その名の通りです。アクセスを拒否します。
- Challenge:JavaScriptが正常に動くかを確認した上でアクセスを許可します。bot避けです。CAPTCHAと違って、ユーザに操作を要求しません。
- CAPTCHA:よくある「ロボットではありませんチェック」です。信号機の画像を選択してください、みたいなのに成功すればアクセスを許可します。
- Count:ログに記録した上で、スルーします。ルールグループ内でこのルールだけ無効化したい、みたいな場合に設定することが多いです。
- Allow:問題なしとして合致した時点でアクセスを許可します。後続チェックをすべてスキップするので、基本的にカスタムルールでしか使いません。
ちなみにMonetizeという6つ目の選択肢は、料金を請求した上でアクセスを許可できるようです。つい最近(2026年6月)追加されたようです。

接続元IP / 国の制御方法
接続元IP/国の制御にはカスタムルールが必要になります。
「カスタムルール」を選択すると、まさにその通りの選択肢が表示されます。

例えば「IPベースのルール」を選択すると以下のような画面に遷移します。
国であれば「地域ベースのルール」を選択すれば良いです。
UIがわかりやすいので細かい説明は割愛します。

カスタムルールでは上記だけでなく、リクエストボディやクエリ文字列、Cookieなども見ることができます。サイズチェックや文字列の一致はもちろん、正規表現を用いてのチェックも可能です。
AND条件、OR条件、NOT条件も設定できるので、割と自由度高く設定ができるのではないでしょうか。
カスタムルールも複雑さに応じてキャパシティが設定されます。合計値には気をつけましょう。
とはいえ「日本国内からのアクセスを許可する」程度であれば、キャパシティは1です。誤差ですね。
ログの保管先
ログの保管先にはCloudWatch Logs、Amazon S3、Firehoseが選択できます。
例えばS3に保管した場合には、以下のようなフォルダ構成でgz形式のファイルが保存されます。
[BucketName]/[TopFolder]/[AccountID]/WAFLogs/[Region]/[WebACLName]/2026/04/02/02/10/
上記であれば2026年4月2日2時10分~14分のログが出力されるフォルダです。5分単位で別のフォルダが作成されます。
ちなみに2時10分は日本時間ではなくUTCです。
1日あたり、00時00分~23時55分までのフォルダが作成されます。
WAFのログは保護パックが処理したアクセスのログです。つまり何が言いたいかというと、割り当てたリソースごとに出力先を分ける、みたいなことはできません。
ただログの中身を見れば手がかりはあります。例えばロードバランサーであれば、”httpSourceId”の値からリソースを区別することができます。
ログの読み方
以下が実際に出るログの例です。(※セキュリティを考慮してマスクはしています)
見やすいように改行も入れて整形していますが、このようなjsonが1行で出てきます。
{
"timestamp": 1747356070337,
"formatVersion": 1,
"webaclId": "arn:aws:wafv2:xxxxxx",
"terminatingRuleId": "AWS-AWSManagedRulesCommonRuleSet",
"terminatingRuleType": "MANAGED_RULE_GROUP",
"action": "BLOCK",
"terminatingRuleMatchDetails": [],
"httpSourceName": "ALB",
"httpSourceId": "xxxxxxxx",
"ruleGroupList": [
{
"ruleGroupId": "AWS#AWSManagedRulesAdminProtectionRuleSet",
"terminatingRule": null,
"nonTerminatingMatchingRules": [],
"excludedRules": null,
"customerConfig": null
},
{
"ruleGroupId": "AWS#AWSManagedRulesAmazonIpReputationList",
"terminatingRule": null,
"nonTerminatingMatchingRules": [],
"excludedRules": null,
"customerConfig": null
},
{
"ruleGroupId": "AWS#AWSManagedRulesAnonymousIpList",
"terminatingRule": null,
"nonTerminatingMatchingRules": [],
"excludedRules": null,
"customerConfig": null
},
{
"ruleGroupId": "AWS#AWSManagedRulesCommonRuleSet",
"terminatingRule": {
"ruleId": "NoUserAgent_HEADER",
"action": "BLOCK",
"ruleMatchDetails": null
},
"nonTerminatingMatchingRules": [],
"excludedRules": null,
"customerConfig": null
}
],
"rateBasedRuleList": [],
"nonTerminatingMatchingRules": [],
"requestHeadersInserted": null,
"responseCodeSent": null,
"httpRequest": {
"clientIp": "xx.xxx.xxx.x",
"country": "JP",
"headers": [
{
"name": "Host",
"value": "xx.xxx.xxx.xxx"
},
{
"name": "Connection",
"value": "Upgrade"
},
{
"name": "Upgrade",
"value": "websocket"
},
{
"name": "Sec-WebSocket-Version",
"value": "13"
},
{
"name": "Sec-WebSocket-Key",
"value": "xxxxxxxxxxxxxxx"
},
{
"name": "x-mbx-apikey",
"value": "xxxxxxxxxxxxxxx"
}
],
"uri": "/ws/btcusdt@bestBidAsk",
"args": "",
"httpVersion": "HTTP/1.1",
"httpMethod": "GET",
"requestId": "xxxxxxxxxxx",
"fragment": "",
"scheme": "https",
"host": "xx.xxx.xxx.xxx"
},
"labels": [
{
"name": "awswaf:managed:aws:core-rule-set:NoUserAgent_Header"
}
],
"ja3Fingerprint": "xxxxxxxxxxx",
"ja4Fingerprint": "xxxxxxxxxxx"
}代表的なところをいくつか紹介します。
“timestamp”:UNIXタイムスタンプで出てきます。
“terminatingRuleId”:terminatingという名前の通り終了ルールです。どのルールでブロックされたのか、どのルールで許可されたのかと言ったことがわかります。ルールグループならルールグループの名前が出てきます。カスタムルールであればカスタムルールの名前が出てきます。
“action”:最終的なアクションです。許可したなら”ALLOW”、ブロックしたなら”BLOCK”が出てきます。結果が出るので、検証続行するCOUNTは出ません。
“ruleGroupList”:検証したルールグループです。ルールグループです。なので、カスタムルールは含まれません。グループではないので。どこかのルールに一致してブロックという結論が出た場合は、それ以降のルールは検証しないため、出てきません。
“ruleGroupList”の下にある”terminatingRule”:これが実際の終了ルールです。上記の例で言うと「”AWS-AWSManagedRulesCommonRuleSet”というルールグループに含まれる”NoUserAgent_HEADER”というルールに合致したためブロックした」ということがわかります。
“ruleGroupList”の下にある”nonTerminatingMatchingRules”:合致したけど終了ではない場合、こちらに入ります。具体的に言うとCountの場合はここに記録されます。