AWS WAFでWebアプリケーションを守るセキュリティ設計入門|マネージドルール・レート制限・地域制限の構成パターン

宮崎智広 この記事の監修:宮崎智広(Linux実務・教育歴20年以上・受講者3,100名超)
HOMELinux技術 リナックスマスター.JP(Linuxマスター.JP)AWS(Amazon Linux) > AWS WAFでWebアプリケーションを守るセキュリティ設計入門|マネージドルール・レート制限・地域制限の構成パターン
「WebアプリをパブリックなALBの後ろに公開したら、毎日大量の不審なリクエストが届く。SQLインジェクションやXSSの攻撃試行をどこで止めればいいか分からない」
「セキュリティグループや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へ移行するのが安全な設計手順


「このままじゃマズい」と感じていませんか?
参考書を開く気力もない、同年代に取り残される不安——
でも安心してください。プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
図解60P/登録10秒/解除も3秒 / 詳細はこちら

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 | # -----------------------------------------------------------------------

運用初期のおすすめ構成は以下の2本です。

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 | # -----------------------------------------------

カウント数が安定して0近辺であれば `OverrideAction: None`(ルール本来のアクション=BLOCK)に変更して本番適用できます。12件のカウントがあった場合は、サンプルリクエスト機能(後述)で実際のリクエスト内容を確認してから判断してください。

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

注意点は、CDNやプロキシ経由のリクエストでは送信元IPの国情報が変わる可能性がある点です。CloudFront配置時はビューワーリクエストの地域コードを使った設定に変更することを推奨します。

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

この設定後は、ALBへの全HTTPSリクエストがWebACLのルールで検査されます。Geo-matchルール・レートベースルール・マネージドルールが優先度順に評価され、どれかにマッチした場合はそのルールのアクションが適用されます。

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"] # }

S3に蓄積されたWAFログはJSON形式で保存されます。Amazon AthenaでSQL形式のクエリ分析が可能です。「過去24時間でブロックされたリクエストのURI上位10件」「特定IPからのSQLi攻撃パターン」等を定期確認することで、ルール改善のサイクルを回せます。

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
AWS WAFの設計の基本は「DefaultAction=ALLOW + 段階的なBLOCKルール追加」と「カウントモードで誤検知を確認してからBLOCKへ移行」の2点です。いきなり全ルールをBLOCKモードにせず、マネージドルールをカウントで運用しながら誤検知がないことを確認してから本番適用するのが、運用障害を防ぐための鉄則です。

設計の3原則は「マネージドルールから始める(自前ルールは最小限に)」「カウントモードで段階的にBLOCKへ移行する」「WAFログをS3に保存して定期的に監査する」です。WAFはSGやNACLと組み合わせた多層防御を形成し、AWSのWebアプリを本番レベルで守るための重要な構成要素です。

AWSでLinuxサーバーを構築する実践的なロードマップについては、AWSをLinuxエンジニアが学ぶためのロードマップもご覧ください。

AWS WAFを「設計できる」エンジニアになりませんか?

マネージドルールの追加手順は調べれば分かります。でも「どのルールを選ぶか」「カウントモードからBLOCKへの移行タイミングをどう判断するか」を現場で即答できますか?
現場で通用する安全なLinuxサーバー構築の「型」を体系的に身につけたい方へ、『Linuxサーバー構築入門マニュアル(図解60P)』を完全無料でプレゼントしています。

「独学の時間がもったいない」「プロから直接、現場の技術を最短で学びたい」という本気の方には、2日で実務レベルのスキルが身につく【初心者向けハンズオンセミナー】も開催しています。

無料メルマガで学習を続ける

Linuxの実践スキルをメールで毎週お届け。
登録は1分、解除もいつでも可。

登録無料・いつでも解除できます

暗記不要・1時間後にはサーバーが動く

3,100名以上が実践した「型」を無料で公開中

プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
その「型」を図解60Pにまとめた入門マニュアルを、完全無料でプレゼントしています。

登録10秒/合わなければ解除3秒 / 詳細はこちら

Linux無料マニュアル(図解60P) 名前とメールで30秒登録
宮崎 智広

この記事を書いた人

宮崎 智広(みやざき ともひろ)

株式会社イーネットマーキュリー代表。現役のLinuxサーバー管理者として20年以上の実務経験を持ち、これまでに累計3,100名以上のエンジニアを指導してきたLinux教育のプロフェッショナル。「現場で本当に使える技術」を体系的に伝えることをモットーに、実践型のLinuxセミナーの開催や無料マニュアルの配布を通じてLinux人材の育成に取り組んでいる。

趣味は、キャンプにカメラ、トラウト釣り。好きな食べ物は、ラーメンにお酒。休肝日が作れない、酒量を減らせないのが悩み。最近、ドラマ「フライトエンジェル」を観て涙腺が崩壊しました。