Skip to main content
emnode / learn
Compliance Medium severity

AWS Security Hub · WAF

WAF.10: WAFv2 web ACLs should have a rule or rule group

Written and reviewed by Emnode · Last reviewed

What does AWS Security Hub WAF.10 check?

WAF.10 checks whether a WAFv2 web ACL contains at least one rule or rule group. It reports FAILED when the web ACL is empty. WAFv2 web ACLs are scoped to either `REGIONAL` (ALB, API Gateway, AppSync, Cognito, App Runner) or `CLOUDFRONT` (managed from us-east-1).

Why does WAF.10 matter?

An empty web ACL still has a default action — usually `Allow` — that every request falls through to because nothing is inspecting it first. So it passes 100% of traffic untouched while appearing, in the console and on architecture diagrams, as a deployed firewall. This typically happens when a web ACL is created ahead of its rules, scaffolded from IaC with an empty rules block, or has its last rule removed during troubleshooting.

How do I fix WAF.10?

  1. Inventory empty web ACLs across both scopes and all regions — `--scope` is required on nearly every `wafv2` call.
  2. Attach the AWS Managed Rules baseline, in Count mode first on production.
  3. Promote the rules to enforcing once the metrics are clean.
  4. Lock the rules baseline into IaC so empties can't be reintroduced.

Remediation script · bash

# Attach the AWS Managed Rules common baseline to an empty web ACL, in Count mode.
# update-web-acl REPLACES the entire Rules array, so supply the full desired set and the current LockToken.
aws wafv2 update-web-acl \
  --scope REGIONAL --name public-alb-waf --id a1b2c3d4-0000-1111-2222-3333 \
  --lock-token e4f5g6h7 --default-action Allow={} \
  --visibility-config SampledRequestsEnabled=true,CloudWatchMetricsEnabled=true,MetricName=public-alb-waf \
  --rules '[{"Name":"AWS-CommonRuleSet","Priority":0,"Statement":{"ManagedRuleGroupStatement":{"VendorName":"AWS","Name":"AWSManagedRulesCommonRuleSet"}},"OverrideAction":{"Count":{}},"VisibilityConfig":{"SampledRequestsEnabled":true,"CloudWatchMetricsEnabled":true,"MetricName":"AWS-CommonRuleSet"}}]'

# Associate a baseline web ACL with an unprotected API Gateway stage (the resource ARN is the stage, not the API).
aws wafv2 associate-web-acl \
  --web-acl-arn arn:aws:wafv2:us-east-1:111122223333:regional/webacl/prod-api-baseline/1a2b3c4d \
  --resource-arn arn:aws:apigateway:us-east-1::/restapis/a1b2c3d4e5/stages/prod

# Protect a Network Firewall from accidental deletion.
aws network-firewall update-firewall-delete-protection \
  --firewall-name prod-egress-inspection --delete-protection

Full walkthrough (console steps, edge cases and verification) in the lesson Protect APIs and edge with WAF.

Is WAF.10 a false positive?

Getting the `--scope` wrong returns an empty list rather than an error, so a web ACL that lives in the other scope can look non-existent when it's simply being queried in the wrong place.

Part of the learning path Lock down access
  • WAF.1 WAF Classic global web ACL logging
  • WAF.2 WAF Classic regional rules should have a condition
  • WAF.3 WAF Classic regional rule groups should have a rule
  • WAF.4 WAF Classic regional web ACLs should have a rule
  • WAF.6 WAF Classic global rules should have a condition
  • WAF.7 WAF Classic global rule groups should have a rule
  • WAF.8 WAF Classic global web ACLs should have a rule
  • WAF.11 WAFv2 web ACL logging should be enabled