Skip to main content
emnode / learn
Compliance

Protect APIs and edge with WAF

One capability across AWS WAF and Network Firewall: make sure the firewalls in front of your APIs, load balancers and VPC traffic actually contain rules and are attached to the resources they are meant to protect, rather than being deployed in name only.

14 min·10 sections·AWS

Last reviewed

WAF and edge protection: the basics

Why a firewall that exists is not the same as a firewall that works

AWS gives you two managed firewall layers for inbound and inter-VPC traffic. AWS WAF (web application firewall) inspects HTTP(S) requests through a web ACL attached to an Application Load Balancer, API Gateway stage, AppSync API or CloudFront distribution; its rules match SQL injection, cross-site scripting, bad bots, rate floods and the AWS Managed Rules baseline. AWS Network Firewall is a stateful firewall that filters traffic flowing to, from and between VPCs through rule groups attached to a firewall policy. Both only protect what they are configured and attached to inspect.

AWS Security Hub turns the failure modes into separate controls, but they share one theme: a firewall that is present but inert. WAF.2, WAF.3, WAF.4, WAF.6, WAF.7, WAF.8 and WAF.10 check that web ACLs and rule groups actually contain rules; APIGateway.4 and ELB.16 check that an API stage and a load balancer are associated with a web ACL at all. On the network side, NetworkFirewall.3, NetworkFirewall.4, NetworkFirewall.5, NetworkFirewall.6 check that the policy and stateless rule groups have rules and a sane default action, and NetworkFirewall.9 and NetworkFirewall.10 check that the firewall has deletion and subnet-change protection so it cannot be removed by accident.

The reason this matters is false assurance. An empty web ACL still appears, in the console and on the architecture diagram, as a deployed firewall, while every request passes straight through on its default action. A load balancer with no associated web ACL looks fronted by WAF on paper but inspects nothing. The job is to make every firewall in the estate carry real rules, be attached to the resource it protects, and be hard to delete by mistake, so that "firewall deployed" actually means "traffic is being inspected."

In this lesson you will learn how AWS WAF and Network Firewall express protection, why an empty or unattached firewall is worse than none because it creates false assurance, and how to find and fix inert firewalls across your accounts and Regions. The Controls this lesson covers section lists every Security Hub control in this capability, each linking to a deep page with the exact check and a copy-and-paste fix.

Fun fact

The firewall that blocked nothing for a year

A SaaS team passed three consecutive security reviews on the strength of "AWS WAF protecting all public endpoints." During an incident eleven months in, a responder ran get-web-acl on the load balancer's web ACL to see which rule had blocked an attacker, and found the Rules array was simply empty. A Terraform module had commented out the rule blocks during an early debugging session and never restored them, so for nearly a year every request had sailed through on the default Allow action. The fix was four minutes: attach the AWS Managed Rules common rule set. The harder fix was explaining how it passed three reviews.

Finding inert firewalls across an estate

Diego runs cloud security at a mid-sized fintech. Security Hub flags a cluster of WAF and Network Firewall findings: empty web ACLs, a public API with no association, and a firewall missing deletion protection.

Rather than work the findings one by one, he starts by pulling each web ACL's rule count, so he can separate firewalls that inspect nothing from those that are genuinely doing their job before changing anything.

Pull a web ACL's definition and check the Rules array. An empty array with a default action of Allow means every request passes uninspected.

$ aws wafv2 get-web-acl --scope REGIONAL --name public-alb-waf --id a1b2c3d4-0000-1111-2222-3333 --query 'WebACL.{Rules:Rules,Default:DefaultAction}'
{
"Rules": [],
"Default": {
"Allow": {}
}
}
# Rules: [] with DefaultAction Allow means this web ACL inspects nothing. WAF.10 fails.

An empty Rules array confirms the finding: the firewall is attached but inspecting zero requests. The same pattern applies to Network Firewall policies with no rule groups.

How WAF and Network Firewall enforce traffic rulesdeep dive

A WAFv2 web ACL is scoped either REGIONAL (Application Load Balancer, API Gateway, AppSync, Cognito) or CLOUDFRONT (managed from us-east-1). The scope is a required parameter on nearly every wafv2 call, and getting it wrong returns an empty list rather than an error, which is a common reason people think a web ACL is missing. Every web ACL has a DefaultAction (Allow or Block) that applies to any request no rule matches, so an empty web ACL with DefaultAction Allow is functionally open. The controls that check for rules evaluate only whether the array is non-empty; they do not judge whether the rules are good.

Updates use optimistic concurrency: update-web-acl replaces the entire Rules array (it is not additive) and requires the current LockToken, so you must supply the full desired rule set on every update, which is why most teams manage this through infrastructure-as-code. Association is a separate step: associate-web-acl binds a web ACL ARN to a resource ARN (for API Gateway, the stage ARN, not the API), and the APIGateway.4 and ELB.16 controls check that this binding exists at all.

AWS Network Firewall carries three independent protection flags (DeleteProtection, SubnetChangeProtection, FirewallPolicyChangeProtection) checked by NetworkFirewall.9, NetworkFirewall.10 and the policy-change sibling, plus checks that the firewall policy and stateless rule groups actually contain rules and define a sensible default stateless action. Network Firewall has no soft-delete and no recycle bin, so deletion protection turns an irreversible single command into a deliberate two-step action. As with WAF, the durable fix lives in the IaC template: CloudFormation and Terraform both default these flags to false, so a flag flipped by hand reverts on the next deploy unless the template encodes it.

What is the impact of an inert or unattached firewall?

The direct impact is the absence of inspection. An empty web ACL is a passthrough: with the common DefaultAction Allow, every injection attempt, scanner probe and credential-stuffing burst reaches your application as if no WAF existed. A public API with no associated web ACL has the same exposure. On the network side, a firewall policy with no rules, or a deletion-protection gap that lets the firewall vanish, leaves VPC egress flowing uninspected, which is exactly the condition that turns a routine mistake into a data-exfiltration window.

The more dangerous impact is false assurance. Unlike having no firewall, an empty or unattached one actively misleads everyone downstream: the console shows it associated, the diagram shows a firewall, the security questionnaire is answered yes, and the compliance report counts the control as present. Every one of those is technically accurate and all are wrong about what matters, whether traffic is being inspected. This is how inert firewalls survive multiple security reviews undetected.

There is a cost angle that sharpens the point. With per-request billing on API Gateway and Lambda, an automated storm against an unprotected endpoint can produce a five-figure invoice spike overnight, with no business activity behind it; a WAF rate-based rule would have capped it for a few dollars. And on the compliance side these controls map to NIST 800-53 configuration and information-flow requirements, so an inert firewall reads as a control that was funded, deployed and then left non-functional, which auditors treat harshly as a process failure rather than a mere gap.

How do you make firewalls actually inspect traffic?

Work the capability as one loop rather than chasing individual findings. The order matters: inventory inert firewalls, attach a baseline safely, promote it to enforcing once metrics are clean, then lock the baseline into infrastructure-as-code so empties cannot return.

1. Inventory empty, unattached and unprotected firewalls

Across both WAF scopes (REGIONAL in every Region, CLOUDFRONT from us-east-1) and all accounts, list web ACLs and check the rule count, list API Gateway stages and load balancers and check for an associated web ACL, and list Network Firewall firewalls and read their protection flags and policy rule counts. Prioritise resources that are internet-facing and currently passing live traffic uninspected.

2. Attach the managed baseline in Count mode first

The safest starting rule set for a web ACL is the AWS Managed Rules common rule set (AWSManagedRulesCommonRuleSet from the AWS vendor), plus a rate-based rule capping a single source IP. Add it with an OverrideAction of Count so it observes and reports matches without blocking, which guarantees you cannot break a production application by suddenly inspecting its traffic. For an unprotected API or load balancer, reuse a shared baseline web ACL rather than creating one per resource.

3. Promote to enforcing once metrics are clean

Watch the CloudWatch counted matches for a day or two. If legitimate traffic is being matched, tune with rule-group overrides before flipping the OverrideAction from Count to None so the managed rules enforce. Never flip straight to enforcing on a high-traffic production endpoint without an observation window. For Network Firewall, enable deletion and subnet-change protection (instant, no traffic impact) and confirm the policy carries real rules and a sane default stateless action.

4. Bake the baseline into infrastructure-as-code and guard it

Most inert firewalls are born in Terraform, CloudFormation or CDK where the rules block was left empty, the association step was forgotten, or the protection flags defaulted to false. Make a non-empty rules block, a web ACL association, and DeleteProtection true required parts of any firewall or API module, and enforce them with AWS Config rules or a CI policy check so a pull request that creates an inert firewall fails before it merges.

# 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

Quick quiz

Question 1 of 5

Security Hub shows a mix of empty WAF web ACLs, a public API with no web ACL, and a Network Firewall missing deletion protection. What is the most efficient way to think about them?

You can now treat WAF and edge protection as one capability rather than a scatter of findings: inventory the firewalls that are empty, unattached or unprotected, attach the managed baseline in Count mode and promote it once metrics are clean, associate web ACLs wherever they are missing, protect network firewalls from accidental deletion, and lock the whole pattern into infrastructure-as-code. The Controls this lesson covers section below links every control in this group to its deep page and fix.

Back to the library

Controls this lesson covers

One capability, many AWS Security Hub controls. This lesson is the shared playbook; each control below keeps its own deep page with the exact check, severity and a copy-and-paste fix.