「セキュリティグループやNACLではL4(IP・ポート)までしか制御できないと聞いた。HTTPリクエストの中身はどう検査すればいい?」
こういった悩みは、AWSでWebアプリの本番環境を初めて構築するエンジニアからよく聞きます。セキュリティグループは「IPアドレスとポート番号」で通信を制御しますが、HTTPリクエストのパス・クエリパラメータ・ボディを検査することはできません。SQLインジェクションやクロスサイトスクリプティング(XSS)のような「正常なポートを使った悪意のあるリクエスト」は素通りしてしまいます。
この記事では、AWS WAFを使ってWebアプリのL7レイヤーを守るセキュリティ設計を、AWS CLIを使って実践的に解説します。WebACLの設計思想からAWSマネージドルールの有効化・レート制限・Geo-matchルールの設定・WAFログのS3出力設計まで、本番運用に必要な構成パターンを一通り押さえます。
動作確認環境: Amazon Linux 2023 / AWS CLI 2.17.x / AWS WAF v2(Regional WebACL for ALB)
この記事のポイント
・AWS WAFはSGと異なりHTTPリクエストの中身(L7)を検査してブロック判定できる
・WebACLにマネージドルールを追加するだけでSQLi・XSS・Bot攻撃を即座にブロック可能
・レートベースルールで同一IPからの過剰アクセスを5分単位で自動遮断できる
・まずカウントモードで誤検知を確認し、段階的にBLOCKへ移行するのが安全な設計手順
でも安心してください。プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
AWS WAFとは何か(SGとの違いとL7防御の必要性)
AWSのネットワーク防御は複数のレイヤーで構成されます。・セキュリティグループ(SG): L4防御。IPアドレスとポート番号でインバウンド/アウトバウンドを制御する。HTTPSポート(443)を許可した場合、その中のHTTPリクエストの内容は検査できない
・NACL(Network ACL): L4防御。VPCサブネット単位のステートレスなフィルタ。SGと同様にL7は検査できない
・AWS WAF: L7防御。HTTPリクエストのURI・クエリパラメータ・ヘッダー・ボディを検査してブロック/許可を判断する
攻撃者は443番ポートを使って正規のHTTPSリクエストを送ることでSGの防御を素通りします。WAFはこのL7層の攻撃を検知・ブロックするために使います。WAFが検査する主な攻撃パターンは以下の通りです。
・SQLインジェクション: クエリパラメータやリクエストボディに悪意のあるSQL文を挿入してDBを操作しようとする攻撃
・クロスサイトスクリプティング(XSS): リクエストに悪意のあるスクリプトを埋め込んでブラウザで実行させる攻撃
・レートベース攻撃(DoS/パスワードスプレー): 同一IPから短時間に大量のリクエストを送って負荷をかける・ブルートフォースする攻撃
・Botトラフィック: スクレイピングBot・脆弱性スキャナーなどの自動化トラフィック
WAFはCloudFront・ALB・API Gateway・AppSyncのいずれかに「WebACL(Web Access Control List)」を関連付ける形で動作します。今回はALBへのWebACL関連付けを例に説明します。
AWSでLinuxサーバーを構築する基礎から学びたい方には、Amazon Linuxの入門教材・学習ロードマップもあわせてご覧ください。
WebACLとルールグループの設計思想
1. WebACLの基本構造
WebACLはWAFの最上位概念で、以下の要素で構成されます。・ルール: 個別の検査ロジック(例:「SQLインジェクション検出パターンにマッチしたらブロック」「同一IPから5分間に1000リクエスト超でブロック」)
・ルールグループ: 複数のルールをまとめたセット。AWSが提供する「マネージドルールグループ」とユーザー定義の「カスタムルールグループ」がある
・スコープ: REGIONAL(ALB・API Gateway向け)またはCLOUDFRONT(CloudFront向け)。CloudFront向けWebACLはus-east-1で作成が必要な点に注意
・DefaultAction: どのルールにもマッチしなかったリクエストの処理。ALLOW(通す)またはBLOCK(遮断)を選ぶ
2. ルール優先度と DefaultAction の設計方針
ルールは優先度(Priority)の小さい番号から順に評価されます。最初にマッチしたルールのアクション(ALLOW/BLOCK/COUNT)が適用され、以降のルールは評価されません。設計の鉄則は「DefaultAction=ALLOW + 明示的なBLOCKルール」です。信頼できるトラフィックのパターンが多様なWebアプリで最初から「BLOCK all by default」にすると、正当なリクエストを過剰にブロックするリスクがあります。ALLOWを基本として、BLOCKルールを段階的に追加していく設計が安全です。
AWSマネージドルールを有効化する手順
1. WebACLを作成する
まずALB向け(REGIONAL)のWebACLを作成します。ルールは後から追加するため、最初は空で作成します。# WAFv2 WebACLをALB向けに作成(DefaultAction=ALLOW) aws wafv2 create-web-acl \ --name "webapp-prod-waf" \ --scope REGIONAL \ --region ap-northeast-1 \ --default-action Allow={} \ --visibility-config \ SampledRequestsEnabled=true,\ CloudWatchMetricsEnabled=true,\ MetricName=webapp-prod-waf \ --rules '[]' # 出力例(WebACL ID と ARN を控えておく) # { # "Summary": { # "Name": "webapp-prod-waf", # "Id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", # "ARN": "arn:aws:wafv2:ap-northeast-1:123456789012:regional/webacl/webapp-prod-waf/a1b2c3d4-...", # "LockToken": "xxxx-lock-token-yyyy" # } # }
2. AWSManagedRulesCommonRuleSetを追加する
AWSが提供するマネージドルールグループの中で最も重要なのが `AWSManagedRulesCommonRuleSet`(CRS: Core Rule Set)です。SQLインジェクション・XSS・LFI(ローカルファイルインクルード)・PHPインジェクション・一般的なWebアプリ攻撃パターンを網羅しています。WebACLを更新するには毎回最新の `LockToken` が必要です。これはAWSがWebACLの競合更新を防ぐための楽観的ロック機構です。
# 最新のLockTokenを取得 WEBACL_ID="a1b2c3d4-e5f6-7890-abcd-ef1234567890" LOCK_TOKEN=$(aws wafv2 get-web-acl \ --name "webapp-prod-waf" \ --scope REGIONAL \ --region ap-northeast-1 \ --id "${WEBACL_ID}" \ --query 'LockToken' \ --output text) echo "LockToken: ${LOCK_TOKEN}" # LockToken: 1234abcd-lock-token-xxxx # AWSManagedRulesCommonRuleSetを追加(Priority 0) aws wafv2 update-web-acl \ --name "webapp-prod-waf" \ --scope REGIONAL \ --region ap-northeast-1 \ --id "${WEBACL_ID}" \ --default-action Allow={} \ --lock-token "${LOCK_TOKEN}" \ --visibility-config \ SampledRequestsEnabled=true,\ CloudWatchMetricsEnabled=true,\ MetricName=webapp-prod-waf \ --rules '[ { "Name": "AWSManagedRulesCommonRuleSet", "Priority": 0, "OverrideAction": {"None": {}}, "Statement": { "ManagedRuleGroupStatement": { "VendorName": "AWS", "Name": "AWSManagedRulesCommonRuleSet" } }, "VisibilityConfig": { "SampledRequestsEnabled": true, "CloudWatchMetricsEnabled": true, "MetricName": "AWSManagedRulesCommonRuleSet" } } ]'
3. 利用可能なマネージドルールグループを確認する
AWSが提供するマネージドルールグループの一覧は以下のコマンドで確認できます。運用初期はCRS(CommonRuleSet)とSQLi特化のSQLiRuleSetの2本から始めるのがおすすめです。# AWS提供のマネージドルールグループ一覧を確認 aws wafv2 list-available-managed-rule-groups \ --scope REGIONAL \ --region ap-northeast-1 \ --query 'ManagedRuleGroups[?VendorName==`AWS`].[Name,Description]' \ --output table # 出力例(主要なグループを抜粋) # ----------------------------------------------------------------------- # | AWSManagedRulesCommonRuleSet | Core rule set (CRS) | # | AWSManagedRulesSQLiRuleSet | SQL injection attacks | # | AWSManagedRulesKnownBadInputsRuleSet| Known bad inputs | # | AWSManagedRulesBotControlRuleSet | Bot traffic detection | # | AWSManagedRulesAmazonIpReputationList | IP reputation list | # -----------------------------------------------------------------------
・Priority 0: AWSManagedRulesCommonRuleSet(XSS・LFI・RFI・PHPインジェクション等を網羅)
・Priority 1: AWSManagedRulesSQLiRuleSet(SQLインジェクションに特化した強化ルール)
レート制限ルールで大量リクエストをブロックする
1. レートベースルールの設定
レートベースルールは「同一IPから5分間に指定回数を超えたらブロック」する仕組みです。ブルートフォース攻撃・クレデンシャルスタッフィング・単純なDoS対策に有効です。設定可能な最小値は100リクエスト/5分です。# LockTokenを再取得してから update-web-acl でレートルールを追加 LOCK_TOKEN=$(aws wafv2 get-web-acl \ --name "webapp-prod-waf" --scope REGIONAL \ --region ap-northeast-1 --id "${WEBACL_ID}" \ --query 'LockToken' --output text) # 既存ルール(CommonRuleSet)に加えてレートベースルールを追加(Priority 2) aws wafv2 update-web-acl \ --name "webapp-prod-waf" \ --scope REGIONAL \ --region ap-northeast-1 \ --id "${WEBACL_ID}" \ --default-action Allow={} \ --lock-token "${LOCK_TOKEN}" \ --visibility-config SampledRequestsEnabled=true,CloudWatchMetricsEnabled=true,MetricName=webapp-prod-waf \ --rules '[ { "Name": "AWSManagedRulesCommonRuleSet", "Priority": 0, "OverrideAction": {"None": {}}, "Statement": {"ManagedRuleGroupStatement": {"VendorName": "AWS", "Name": "AWSManagedRulesCommonRuleSet"}}, "VisibilityConfig": {"SampledRequestsEnabled": true, "CloudWatchMetricsEnabled": true, "MetricName": "CommonRuleSet"} }, { "Name": "RateLimitRule", "Priority": 2, "Action": {"Block": {}}, "Statement": { "RateBasedStatement": { "Limit": 1000, "AggregateKeyType": "IP" } }, "VisibilityConfig": {"SampledRequestsEnabled": true, "CloudWatchMetricsEnabled": true, "MetricName": "RateLimitRule"} } ]'
2. カウントモードで誤検知を事前確認する
本番でいきなりBLOCKすると正当なリクエストを遮断するリスクがあります。まずマネージドルールグループを `OverrideAction: Count` でカウントモードにし、CloudWatchでブロック対象リクエストを把握してから本番BLOCKへ切り替えるのが安全な手順です。# カウントモードにする場合: OverrideAction を Count に変更 # "OverrideAction": {"Count": {}} # CloudWatchでカウント数を確認(直近1時間分) aws cloudwatch get-metric-statistics \ --namespace AWS/WAFV2 \ --metric-name CountedRequests \ --dimensions Name=WebACL,Value=webapp-prod-waf \ Name=Region,Value=ap-northeast-1 \ Name=Rule,Value=AWSManagedRulesCommonRuleSet \ --start-time $(date -u -d "1 hour ago" +%Y-%m-%dT%H:%M:%S) \ --end-time $(date -u +%Y-%m-%dT%H:%M:%S) \ --period 300 \ --statistics Sum \ --output table # 出力例(5分ウィンドウ別のカウント数) # ----------------------------------------------- # | Timestamp | Sum | # +-------------------------+-------------------+ # | 2026-07-09T02:00:00Z | 12.0 | # | 2026-07-09T02:05:00Z | 3.0 | # | 2026-07-09T02:10:00Z | 0.0 | # -----------------------------------------------
Geo-matchルールで不要な地域からのアクセスをブロック
日本のユーザーしか利用しないサービスであれば、日本(JP)以外の国からのリクエストをブロックすることで攻撃トラフィックを大幅に削減できます。# Geo-matchルール(JP以外をブロック)の定義例 # ルール配列に以下を追加(Priority 3) { "Name": "GeoBlockNonJP", "Priority": 3, "Action": {"Block": {}}, "Statement": { "NotStatement": { "Statement": { "GeoMatchStatement": { "CountryCodes": ["JP"] } } } }, "VisibilityConfig": { "SampledRequestsEnabled": true, "CloudWatchMetricsEnabled": true, "MetricName": "GeoBlockNonJP" } } # 設定後、直近24時間でブロックされたリクエスト数を確認 aws cloudwatch get-metric-statistics \ --namespace AWS/WAFV2 \ --metric-name BlockedRequests \ --dimensions Name=WebACL,Value=webapp-prod-waf \ Name=Region,Value=ap-northeast-1 \ Name=Rule,Value=GeoBlockNonJP \ --start-time $(date -u -d "24 hours ago" +%Y-%m-%dT%H:%M:%S) \ --end-time $(date -u +%Y-%m-%dT%H:%M:%S) \ --period 3600 \ --statistics Sum \ --output table
Linuxサーバーでポート開放状況を確認する際は、Linux ポート確認の全コマンドもあわせてご参照ください。
CloudFront・ALBへのWebACLを関連付ける
WAFのWebACLを作成しただけではトラフィックは検査されません。CloudFrontまたはALBのARNを指定してWebACLを関連付けることで、初めて防御が有効になります。# ALBのARNを取得 ALB_ARN=$(aws elbv2 describe-load-balancers \ --names "webapp-prod-alb" \ --query 'LoadBalancers[0].LoadBalancerArn' \ --output text) echo "ALB ARN: ${ALB_ARN}" # ALB ARN: arn:aws:elasticloadbalancing:ap-northeast-1:123456789012:loadbalancer/app/webapp-prod-alb/abcdef1234567890 # WebACLをALBに関連付け WEBACL_ARN="arn:aws:wafv2:ap-northeast-1:123456789012:regional/webacl/webapp-prod-waf/a1b2c3d4-e5f6-7890-abcd-ef1234567890" aws wafv2 associate-web-acl \ --web-acl-arn "${WEBACL_ARN}" \ --resource-arn "${ALB_ARN}" \ --region ap-northeast-1 # 関連付けを確認 aws wafv2 get-web-acl-for-resource \ --resource-arn "${ALB_ARN}" \ --region ap-northeast-1 \ --query 'WebACL.Name' \ --output text # 出力例(関連付け成功) # webapp-prod-waf
AWSインフラ設計のより実践的な構成パターンについては、AWSクラウド実践設計講座もご覧ください。
WAFログをS3に送信して監査する
WAFのブロック・カウントログはAmazon Kinesis Data Firehose経由でS3に送信できます。セキュリティ監査やインシデント調査のためにログを保存しておくことは、本番運用の必須要件です。# Kinesis Firehose 経由でS3ログ出力を有効化 # ※Firehoseの配信ストリーム名は必ず "aws-waf-logs-" プレフィックスで始める(WAFの要件) FIREHOSE_ARN="arn:aws:firehose:ap-northeast-1:123456789012:deliverystream/aws-waf-logs-webapp-prod" aws wafv2 put-logging-configuration \ --logging-configuration \ "ResourceArn=${WEBACL_ARN},LogDestinationConfigs=[${FIREHOSE_ARN}]" \ --region ap-northeast-1 # ログ設定確認 aws wafv2 get-logging-configuration \ --resource-arn "${WEBACL_ARN}" \ --region ap-northeast-1 \ --query 'LoggingConfiguration.{WebACL:ResourceArn,LogDest:LogDestinationConfigs}' # 出力例 # { # "WebACL": "arn:aws:wafv2:ap-northeast-1:123456789012:regional/webacl/webapp-prod-waf/...", # "LogDest": ["arn:aws:firehose:ap-northeast-1:123456789012:deliverystream/aws-waf-logs-webapp-prod"] # }
AWSのVPCプライベートサブネット内のDNS設定について確認が必要な場合は、Linux DNS 設定の基本も参照してください。
トラブルシュート|正当なリクエストがブロックされる場合
「403 Forbidden」が返ってくる場合の確認手順
WAFを有効化した後に正当なユーザーから「403が返る」という報告があった場合、以下の順番で確認します。・サンプルリクエストを確認する: WAFコンソール「Sampled requests」またはCLIでブロックされたリクエストとヒットしたルールを確認する
・問題のルールをカウントモードに変更: `OverrideAction: Count` に一時切替して403が解消するか確認する
・特定のパスをルールから除外: `ExcludedRules` でルール内の個別ルールをカウントに固定する
・IPセットホワイトリストを追加: 社内オフィスのIPを最優先ALLOWルール(Priority 0より小さい番号)に追加する
# ブロックされたリクエストのサンプルを確認(直近1時間分) aws wafv2 get-sampled-requests \ --web-acl-arn "${WEBACL_ARN}" \ --rule-metric-name "AWSManagedRulesCommonRuleSet" \ --scope REGIONAL \ --region ap-northeast-1 \ --time-window StartTime=$(date -u -d "1 hour ago" +%Y-%m-%dT%H:%M:%S),EndTime=$(date -u +%Y-%m-%dT%H:%M:%S) \ --max-items 10 \ --query 'SampledRequests[*].{URI:Request.URI,IP:Request.ClientIP,Rule:RuleNameWithinRuleGroup}' \ --output table # 出力例(実際のリクエストURIとクライアントIPが表示される) # ------------------------------------------------------- # | IP | Rule | URI | # +---------------+------------------------+------------+ # | 203.0.113.45 | SizeRestrictions_BODY | /api/upload| # | 198.51.100.12 | CrossSiteScripting_BODY| /comment | # -------------------------------------------------------
「SizeRestrictions_BODY」ルールでファイルアップロードがブロックされる場合
ファイルアップロードAPIなどで大きなリクエストボディを送信する場合、`AWSManagedRulesCommonRuleSet` の `SizeRestrictions_BODY` ルールがブロックすることがあります(デフォルト上限: 8KB)。対処方法は以下の2つです。・ExcludedRulesで特定ルールのみカウントに固定する: `ManagedRuleGroupStatement` の `ExcludedRules` に `SizeRestrictions_BODY` を追加する
・対象パスを明示的なALLOWルールで処理する: `/api/upload` パスのみを優先ALLOW(低番号のPriority)で通す設定を追加する
本記事のまとめ
| やりたいこと | コマンド / 設定 |
|---|---|
| REGIONAL WebACLを作成する | aws wafv2 create-web-acl --scope REGIONAL --default-action Allow={} |
| 最新のLockTokenを取得する | aws wafv2 get-web-acl --name ... --scope REGIONAL --id ... --query LockToken |
| マネージドルールグループを追加する | aws wafv2 update-web-acl --lock-token ... --rules '[{"Statement":{"ManagedRuleGroupStatement":...}}]' |
| 利用可能なマネージドルールを一覧する | aws wafv2 list-available-managed-rule-groups --scope REGIONAL |
| WebACLをALBに関連付ける | aws wafv2 associate-web-acl --web-acl-arn ... --resource-arn ALB_ARN |
| WAFログをFirehose経由でS3に送信する | aws wafv2 put-logging-configuration --logging-configuration ResourceArn=...,LogDestinationConfigs=[...] |
| ブロックされたリクエストのサンプルを確認する | aws wafv2 get-sampled-requests --web-acl-arn ... --rule-metric-name ... --max-items 10 |
| WebACLとALBの関連付けを確認する | aws wafv2 get-web-acl-for-resource --resource-arn ALB_ARN |
設計の3原則は「マネージドルールから始める(自前ルールは最小限に)」「カウントモードで段階的にBLOCKへ移行する」「WAFログをS3に保存して定期的に監査する」です。WAFはSGやNACLと組み合わせた多層防御を形成し、AWSのWebアプリを本番レベルで守るための重要な構成要素です。
AWSでLinuxサーバーを構築する実践的なロードマップについては、AWSをLinuxエンジニアが学ぶためのロードマップもご覧ください。
AWS WAFを「設計できる」エンジニアになりませんか?
マネージドルールの追加手順は調べれば分かります。でも「どのルールを選ぶか」「カウントモードからBLOCKへの移行タイミングをどう判断するか」を現場で即答できますか?
現場で通用する安全なLinuxサーバー構築の「型」を体系的に身につけたい方へ、『Linuxサーバー構築入門マニュアル(図解60P)』を完全無料でプレゼントしています。
「独学の時間がもったいない」「プロから直接、現場の技術を最短で学びたい」という本気の方には、2日で実務レベルのスキルが身につく【初心者向けハンズオンセミナー】も開催しています。
3,100名以上が実践した「型」を無料で公開中
プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
その「型」を図解60Pにまとめた入門マニュアルを、完全無料でプレゼントしています。
登録10秒/合わなければ解除3秒 / 詳細はこちら

無料メルマガで学習を続ける
Linuxの実践スキルをメールで毎週お届け。
登録は1分、解除もいつでも可。
登録無料・いつでも解除できます