Skip to main content
emnode
Compliance Medium severity

AWS Security Hub · WAF

WAF.4: WAF Classic regional web ACLs should have a rule

Written and reviewed by Emnode · Last reviewed

What does AWS Security Hub WAF.4 check?

WAF.4 checks AWS WAF Classic Regional web ACLs (`AWS::WAFRegional::WebACL`, attached to ALBs, API Gateway stages, and AppSync APIs). It reports FAILED when the web ACL contains no rules or rule groups: there's nothing to evaluate, so every request goes straight to the default action.

Why does WAF.4 matter?

An empty web ACL is more dangerous than no web ACL. With the common `ALLOW` default action, every request passes through uninspected while the resource is attached, named, billed, and shown on dashboards as protected. A resource with no WAF is an obvious gap a reviewer catches; an attached web ACL named `prod-alb-waf` looks protected at a glance even though the container is hollow.

How do I fix WAF.4?

  1. Confirm what the empty web ACL protects and what its default action is.
  2. Add at least one meaningful rule or rule group, starting in COUNT mode on production to watch for false positives before enforcing.
  3. Verify the finding clears once the `Rules` array is non-empty.
  4. Migrate off WAF Classic to WAFv2 with a baselined rule set so the gap can't reopen.

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.4 a false positive?

An empty Regional web ACL is occasionally intentional when its default action is `BLOCK`, for example a deny-by-default ACL fronting a resource that should reject all traffic, or a placeholder ACL provisioned by IaC before its rules land but not yet associated with any ALB, API Gateway stage, or AppSync API. WAF.4 only checks that the `Rules` array is non-empty; it does not read the default action or whether the ACL is attached, so a deliberately empty deny-all or an unassociated stub still reports FAILED. If that is the intended state, document the rationale and suppress the finding (or set a Security Hub automation rule to NOT_AFFECTED) rather than adding a token rule purely to satisfy the check.

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.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.10 WAFv2 web ACLs should have a rule or rule group
  • WAF.11 WAFv2 web ACL logging should be enabled