AWS WAFマネージドルールを全有効化したら本番が死んだ話と、1年半で辿り着いた設定戦略
「AWSが用意してるなら安全でしょ」と全部Blockにしたら決済APIが全滅した経験、ありませんか?1年半の試行錯誤で学んだCount/Block移行の現実的な進め方をまとめました。
WAFマネージドルールを全部有効化して本番が死んだ日
正直に言うと、最初にAWS WAF v2を導入したとき、やらかした。
SOC2審査の準備を進めていたチームから「WAFをちゃんとやれ」と言われて、マネージドルールグループを見たら「AWSManaged」が並んでるじゃないですか。「AWSが用意してるなら安全でしょ」って全部Blockモードで有効化したんですよ。その3時間後に決済APIが全滅しました。
アラートが飛んできて確認したら、AWSManagedRulesSQLiRuleSetがJSON bodyのネストしたSQLクエリを含むリクエストを全部ブロックしてた。うちのAPIはレポートクエリのfilter条件をJSONで受け取る設計だったので、正規のリクエストが全滅するのは当然だったんですが、その時は気づかなかった。
そこから1年半、試行錯誤しながら運用してきた知見を書く。以前WAF・DDoS対策の記事でも触れたけど、今回はマネージドルールの設定戦略にフォーカスした話。
2026年時点のWAF v2マネージドルールの全体像
2026年8月現在、AWS WAF v2のマネージドルールグループは大きく以下のカテゴリに整理されている。AWSが提供するものとAWS MarketplaceのISVが提供するものがあるが、うちのチームはまずAWS提供のものだけで固めた。
graph TB
subgraph WAF_v2["AWS WAF v2 - マネージドルールグループ"]
subgraph Core["Coreルールグループ"]
CRS[AWSManagedRulesCommonRuleSet<br/>一般的な脆弱性 700 WCU]
ADMIN[AWSManagedRulesAdminProtectionRuleSet<br/>管理パスへの不正アクセス 100 WCU]
KBI[AWSManagedRulesKnownBadInputsRuleSet<br/>既知の悪意あるリクエスト 200 WCU]
end
subgraph UseCase["ユースケース別"]
SQLI[AWSManagedRulesSQLiRuleSet<br/>SQLインジェクション 200 WCU]
LINUX[AWSManagedRulesLinuxRuleSet<br/>Linux固有の脆弱性 200 WCU]
PHP[AWSManagedRulesPHPRuleSet<br/>PHP固有の脆弱性 100 WCU]
WINDOWS[AWSManagedRulesWindowsRuleSet<br/>Windows固有 200 WCU]
POSIX[AWSManagedRulesPOSIXRuleSet<br/>POSIXシェル 100 WCU]
WORDPRESS[AWSManagedRulesWordPressRuleSet<br/>WordPress固有 100 WCU]
end
subgraph Reputation["評判・Bot"]
IPR[AWSManagedRulesAmazonIpReputationList<br/>Amazonの脅威インテリジェンス 25 WCU]
ANON[AWSManagedRulesAnonymousIpList<br/>Tor/VPN/プロキシ 50 WCU]
BOT[AWSManagedRulesBotControlRuleSet<br/>Bot検出 50-100 WCU]
ATP[AWSManagedRulesATPRuleSet<br/>アカウント乗っ取り防止 50 WCU]
ACFP[AWSManagedRulesACFPRuleSet<br/>アカウント作成詐欺防止 50 WCU]
end
end
WCU(WAF Capacity Unit)の合計が1500を超えるとWebACL自体が作れないので、全部入れようとするとすでに1525 WCU超える。これも気づかなかったポイントのひとつ。
各ルールグループのWCU消費量
xychart-beta
title "WAFルールグループのWCU消費量"
x-axis ["CommonRuleSet", "KBI", "SQLi", "Linux", "BotControl", "AdminProtect", "AnonymousIP", "IP Reputation"]
y-axis "WCU" 0 --> 800
bar [700, 200, 200, 200, 150, 100, 50, 25]
Bot ControlのWCUはTARGETEDモードにすると150 WCU程度になる。全部入れると合計1625 WCUで上限オーバーになるため、うちはWordPressRuleSet(使ってない)とPHPRuleSet(使ってない)は外してある。
2026年の変更点
2025年後半から2026年にかけて特に変化が大きかったのはBot Controlで、Targeted Inspectionという機能が強化された。シグネチャベースの検出から機械学習ベースの検出に切り替えるオプションで、WCUは増えるが誤検知が大幅に減る。あとATP(Account Takeover Protection)がJavaScript統合なしでも一定の検出ができるようになったのも地味に大きい変化だった。
本番で安全にマネージドルールを導入するための段階的アプローチ
失敗から学んで今うちのチームでやっている導入フローはこう。
flowchart TB
A[マネージドルール選定] --> B[Count モードで全ルール有効化]
B --> C[1〜2週間ログ分析]
C --> D{誤検知の確認}
D -->|誤検知多い| E[ルールアクションの上書き設定]
D -->|誤検知なし| F[Block モードに切り替え]
E --> G[除外ルールの設定]
G --> H[再度ログ分析 1週間]
H --> F
F --> I[CloudWatch Metrics監視設定]
I --> J[定期的なチューニング]
style A fill:#FF9900,color:#fff
style F fill:#28a745,color:#fff
style E fill:#dc3545,color:#fff
最重要なのは必ずCountモードから始めること。これはWAFマネージドルール運用の先行記事でも触れているが、本当に大事すぎるので繰り返す。「AWSのマネージドルールだから大丈夫」という信頼は、うちの決済APIを3時間止めた。
Terraformでの設定例(2026年版)
resource "aws_wafv2_web_acl" "main" {
name = "production-web-acl"
scope = "REGIONAL"
default_action {
allow {}
}
# Step 1: まずIPレピュテーションリストはいきなりBlock
# 正規ユーザーがTorやVPNを使うケースは業務システムでは少ない
rule {
name = "AWSManagedRulesAmazonIpReputationList"
priority = 1
override_action {
none {} # マネージドルールのデフォルトアクションを使用
}
statement {
managed_rule_group_statement {
name = "AWSManagedRulesAmazonIpReputationList"
vendor_name = "AWS"
}
}
visibility_config {
cloudwatch_metrics_enabled = true
metric_name = "IpReputationList"
sampled_requests_enabled = true
}
}
# Step 2: CommonRuleSetはCountモードで開始
rule {
name = "AWSManagedRulesCommonRuleSet"
priority = 10
override_action {
count {} # 最初はCountモード!
}
statement {
managed_rule_group_statement {
name = "AWSManagedRulesCommonRuleSet"
vendor_name = "AWS"
# 特定ルールのアクションを個別にCountに上書き
# Body検査はAPIゲートウェイとの相性確認後に有効化
rule_action_override {
name = "SizeRestrictions_BODY"
action_to_use {
count {}
}
}
rule_action_override {
name = "NoUserAgent_HEADER"
action_to_use {
count {}
}
}
}
}
visibility_config {
cloudwatch_metrics_enabled = true
metric_name = "CommonRuleSet"
sampled_requests_enabled = true
}
}
# Step 3: SQLインジェクションもCountから
rule {
name = "AWSManagedRulesSQLiRuleSet"
priority = 20
override_action {
count {}
}
statement {
managed_rule_group_statement {
name = "AWSManagedRulesSQLiRuleSet"
vendor_name = "AWS"
}
}
visibility_config {
cloudwatch_metrics_enabled = true
metric_name = "SQLiRuleSet"
sampled_requests_enabled = true
}
}
visibility_config {
cloudwatch_metrics_enabled = true
metric_name = "production-web-acl"
sampled_requests_enabled = true
}
}
ここで地味に大事なのがSizeRestrictions_BODYとNoUserAgent_HEADERの除外。うちの環境では最初にこれでハマった。Webhookのペイロードが8KBを超えることがあったし、一部の自動化ツールはUser-Agentを設定しないことがある。この2つはほぼ確実に誤検知を踏むので、最初から個別にCountに上書きしておくのが無難だと思う。
ログ分析クエリ(Athena)
Countモードで1週間動かした後、Athenaでこんなクエリを回して誤検知の候補を探す。
-- どのルールが何回引っかかっているか
SELECT
rule_group_list.rule_match_details.rule_id AS matched_rule,
COUNT(*) AS hit_count,
COUNT(DISTINCT httprequest.clientip) AS unique_ips,
MIN(timestamp) AS first_seen,
MAX(timestamp) AS last_seen
FROM
waf_logs
CROSS JOIN UNNEST(rule_group_list) AS t(rgl)
CROSS JOIN UNNEST(rgl.terminating_rule) AS t2(rule_match_details)
WHERE
action = 'COUNT'
AND from_iso8601_timestamp(timestamp) > NOW() - INTERVAL '7' DAY
GROUP BY
rule_group_list.rule_match_details.rule_id
ORDER BY
hit_count DESC
LIMIT 50;
-- 特定ルールで引っかかったリクエストのURLパターン確認
SELECT
httprequest.uri,
httprequest.httpmethod,
COUNT(*) AS count
FROM
waf_logs
WHERE
action = 'COUNT'
AND from_unixtime(timestamp/1000) > NOW() - INTERVAL '7' DAY
-- 調査したいルール名を指定
AND filter(rule_group_list, r -> r.terminating_rule.rule_id = 'SQLi_BODY') != ARRAY[]
GROUP BY
httprequest.uri,
httprequest.httpmethod
ORDER BY
count DESC;
このクエリで「/api/v2/reports/queryが2000回ヒット」みたいな結果が出たら、そのパスは除外対象候補。正規の業務リクエストかどうかを確認して、除外するかAPIの実装を変えるかを判断する。個人的には「APIの設計を変えられるなら変えた方がいい」派だけど、現実的には既存APIを変えられないケースも多いので、その場合は後述のカスタムルールで除外する。
Bot Control Targeted Inspectionの活用(2026年の本番知見)
Bot Controlは正直、最初は懐疑的だった。WCUも食うし、追加料金もかかる。でも不正ログイン試行が週5000件規模になってきたあたりで本格導入に踏み切った。
2026年のBot Control設定
rule {
name = "AWSManagedRulesBotControlRuleSet"
priority = 5
override_action {
none {}
}
statement {
managed_rule_group_statement {
name = "AWSManagedRulesBotControlRuleSet"
vendor_name = "AWS"
managed_rule_group_config {
aws_managed_rules_bot_control_rule_set {
# 2026年から強化されたTargeted Inspectionを使用
inspection_level = "TARGETED"
enable_machine_learning = true
}
}
# 監視系ツール(Datadog、New Relic等)は除外
scope_down_statement {
not_statement {
statement {
regex_match_statement {
field_to_match {
single_header {
name = "user-agent"
}
}
regex_string = "(Datadog|NewRelicPinger|UptimeRobot|StatusCake)"
text_transformations {
priority = 0
type = "LOWERCASE"
}
}
}
}
}
}
}
visibility_config {
cloudwatch_metrics_enabled = true
metric_name = "BotControl"
sampled_requests_enabled = true
}
}
enable_machine_learning = trueは2026年に入ってGAになったオプション。シグネチャだけでは検出できない高度なボットを機械学習で判定してくれる。導入後3ヶ月で不正ログイン試行が97%減少した。ただし誤検知も多少あって、API Gatewayからのヘルスチェックが最初は引っかかった。監視ツールの除外設定は必須と思っておいた方がいい。
本番環境のAWS構成図
うちの本番はこういう構成になっている。
graph TB
subgraph Internet["Internet"]
Users[正規ユーザー]
Bots[Bot・攻撃者]
end
subgraph CloudFront["CloudFront Distribution"]
CF[CloudFront]
WAFCF[WAF WebACL<br/>CloudFront scope]
end
subgraph REGION["ap-northeast-1"]
subgraph WAF_Layer["WAF Layer"]
WAFR[WAF WebACL<br/>Regional scope]
end
subgraph ALB_Layer["ALB"]
ALB[Application Load Balancer]
end
subgraph VPC["VPC 10.0.0.0/16"]
subgraph PublicSubnet["Public Subnet"]
NAT[NAT Gateway]
end
subgraph PrivateSubnet_A["Private Subnet AZ-a"]
ECS_A[ECS Tasks<br/>API Servers]
end
subgraph PrivateSubnet_C["Private Subnet AZ-c"]
ECS_C[ECS Tasks<br/>API Servers]
end
subgraph DataSubnet["Data Subnet"]
RDS[(Aurora PostgreSQL)]
REDIS[ElastiCache Redis]
end
end
subgraph Monitoring["Monitoring"]
CW[CloudWatch Logs<br/>WAF Full Logs]
KF[Kinesis Firehose]
S3L[S3 WAF Logs Bucket]
ATHENA[Athena<br/>ログ分析]
end
subgraph Security["Security"]
GD[GuardDuty]
SM[Security Hub]
SC[Shield Advanced]
end
end
Users --> CF
Bots --> CF
CF --> WAFCF
WAFCF -->|Block| Bots
WAFCF -->|Allow| ALB
ALB --> WAFR
WAFR --> ECS_A
WAFR --> ECS_C
ECS_A --> RDS
ECS_C --> RDS
ECS_A --> REDIS
ECS_C --> REDIS
WAFR --> KF
KF --> S3L
S3L --> ATHENA
GD --> SM
SC --> ALB
CloudFront側とALB側の両方にWAFを配置しているのがポイント。CloudFront側はグローバルスコープで主にIPレピュテーションとBot Controlを担当して、ALB側はRegionalスコープでアプリケーション固有のルールを担当する。二重にしているのは、CloudFrontをバイパスして直接ALBを叩いてくる攻撃に備えるため。ALBのDNSをセキュリティグループで守り切れない場合のバックアップとして機能する。
ルールグループ選定の実務判断基準
1年半運用してわかった、どのルールを有効化すべきかの判断基準をまとめる。
| ルールグループ | 推奨アクション | 注意点 | 対象 |
|---|---|---|---|
| AmazonIpReputationList | Block(即時OK) | 誤検知ほぼなし | 全環境 |
| CommonRuleSet | Count→段階的にBlock | SizeRestrictions_BODYは要確認 | 全環境 |
| KnownBadInputsRuleSet | Block(比較的安全) | Log4Shell等の既知CVEをブロック | 全環境 |
| SQLiRuleSet | Count→Block | JSON APIは誤検知多い | DB接続あり |
| AdminProtectionRuleSet | Block | /admin等のパスへのアクセス制御 | 管理画面あり |
| AnonymousIpList | Count→要判断 | VPN利用ユーザーへの影響を確認 | ケースバイケース |
| BotControlRuleSet | CountのTargetedから | 監視ツールの除外設定必須 | Bot対策必要な場合 |
| ATPRuleSet | Count→Block | JavaScript統合で精度向上 | ログイン機能あり |
| LinuxRuleSet | Block | Linuxサーバーなら基本入れる | Linux環境 |
| PHPRuleSet | Block | PHPアプリのみ | PHP環境 |
正直、AnonymousIpListは難しくて、うちは結局Countのままにしている。在宅勤務でVPN使っているユーザーが普通にいるし、海外からアクセスするケースもある。完全にBlockしてサポートに問い合わせが来るより、ログで監視しておく方が現実的だと判断した。「セキュリティのために不便にする」のは限度があって、ビジネス要件とのバランスが大事だと思う。
誤検知との付き合い方と継続的チューニング
GuardDutyのFalse Positive問題と構造は似ていて、WAFも「ブロックすること」が目的じゃなく「正規トラフィックを通しながら攻撃を防ぐこと」が目的。この当たり前のことを忘れると詰む。最初の俺がまさにそれだった。
カスタムルールで誤検知をコントロールする
# 特定パスをマネージドルールの評価から除外
rule {
name = "ExcludeReportQueryPath"
priority = 0 # マネージドルールより高い優先度
action {
allow {}
}
statement {
and_statement {
statements {
# 特定パスのみ
byte_match_statement {
field_to_match {
uri_path {}
}
positional_constraint = "STARTS_WITH"
search_string = "/api/v2/reports/query"
text_transformations {
priority = 0
type = "NONE"
}
}
}
statements {
# 内部サービスのIPからのみ
ip_set_reference_statement {
arn = aws_wafv2_ip_set.internal_services.arn
}
}
}
}
visibility_config {
cloudwatch_metrics_enabled = true
metric_name = "ExcludeReportQueryPath"
sampled_requests_enabled = true
}
}
重要なのは、除外を「全IP」で設定しないこと。上の例のように「特定パス」かつ「信頼できるIP」という条件を組み合わせることで、攻撃者が除外パスを悪用するリスクを下げられる。「このパスはWAFを全部スキップ」みたいな雑な設定をやってしまうと、せっかくのWAFが穴だらけになる。
ブロック率の推移
Countモード開始から段階的にBlockモードに移行していくにつれ、ブロック率がこんな感じで上がっていった。
xychart-beta
title "WAF導入後のブロック率推移(月次)"
x-axis ["2025-02", "2025-03", "2025-04", "2025-05", "2025-06", "2025-07", "2025-08"]
y-axis "ブロック率(%)" 0 --> 30
line [0, 3.2, 8.1, 11.4, 14.2, 15.8, 16.3]
16%台というのは「正規トラフィックの16%をブロックしている」ではなく「全リクエストの16%が攻撃と判定されてブロックされている」という意味。誤検知(正規トラフィックのブロック)は今は0.01%以下に収まっている。
コスト感と運用負荷の現実
WAF v2のコストはWebACL料金 + ルール料金 + リクエスト料金の合計。うちの規模(月1億リクエスト程度)での実績値を共有する。
| コスト要素 | 月額(目安) |
|---|---|
| WebACL(2個:Regional + CloudFront) | $10 |
| マネージドルールグループ(6個) | $30 |
| リクエスト処理料金(1億リクエスト) | $60 |
| Bot Control追加料金(1億リクエストの一部) | $20 |
| ログ出力(Firehose + S3) | $15 |
| 合計 | 約$135/月 |
月135ドルで本番サービスを守れていると思えば安いけど、最初は「WAFだけでこんなにかかるの」と思った。Shield Advancedは月3000ドルかかるのでまだ導入していない。DDoS対策はAWS Network Firewallとの組み合わせで対応している。
セキュリティ全般のコンプライアンス対応という観点ではSOC2対応記事も参考になる。WAFの設定は監査ログとして残すことが重要で、Full Loggingを必ず有効化しておくこと。
運用負荷について正直に言うと、最初の2〜3ヶ月はチューニングにかなり時間がかかった。週1回はAthenaでログを分析して、誤検知がないかチェックする作業が必要だった。今は月1回の定期レビューで済んでいる。慣れれば重くない作業だけど、「WAFを入れたら終わり」ではないのは覚悟しておいてほしい。
まとめ
1年半の本番運用でわかったWAF v2マネージドルール活用のポイントをまとめると:
- 全有効化・即Blockは絶対NG。必ずCountモードから始めて、最低1〜2週間のログ分析後にBlockへ移行する
- 誤検知ゼロは無理。どの誤検知を許容してどの攻撃を防ぐか、ビジネス要件に基づいて判断する。
SizeRestrictions_BODYとNoUserAgent_HEADERは要注意 - IPレピュテーションリストだけは即Block。誤検知リスクが低く、効果が高い。これだけでもブロック数の半分以上を占める
- Bot Control Targeted Inspectionは2026年時点でかなり実用的になった。不正ログイン試行の多いサービスには投資対効果が高い
- ログ分析基盤(Athena + S3)の整備が運用継続の前提。ログがないとチューニングのしようがない
次のアクションとして、まだWAFを導入していないなら、まずIPレピュテーションリストだけBlockモードで設定するところから始めてみてほしい。5分でできて、即効性がある。皆さんのチームのWAF運用状況はどうですか?誤検知との戦い方、コメントで教えてもらえると嬉しい。