Engineering WAF rules

Lesson 3 of 4 in Protecting the Edge.

A web ACL is an ordered list. Each entry is either a rule you wrote or a rule group (your own, AWS-managed, or bought from AWS Marketplace), and each entry carries a priority — a number whose only job is to establish order. WAF evaluates entries from the lowest priority number upward, and the first terminating action ends evaluation. Allow and Block terminate. Count never does: it records a match, attaches any labels, and evaluation continues. If nothing terminates, the web ACL's default action decides — Allow for a public site, Block for an allow-list design.

That single mechanism explains most of the WAF misconfigurations the exam shows you. An Allow rule for your office CIDR at priority 10 means the managed rule groups at priorities 20 and 30 never see traffic from the office — including an exploit launched from a compromised laptop sitting in it. A Block rule placed after a broad Allow is dead code. A web ACL whose first rule allows a partner's entire address range is, for that partner, a web ACL with no rules at all.

Capacity is the other constraint worth knowing: every rule costs web ACL capacity units (WCUs) according to its complexity, and a web ACL has a budget — 5,000 by default, raisable on request. Regex and body-inspection statements are expensive; IP-set matches are cheap. "Add one more managed rule group" is occasionally an architecture decision rather than a checkbox.

The five rule actions — and whether evaluation continues
ActionTerminating?What the client seesReach for it when

Allow

yes

the request proceeds to the protected resource

you are certain — and you have accepted that it skips every later rule

Block

yes

403 by default, or your custom response code and body

the match is unambiguous and the false-positive rate is measured

Count

no — evaluation continues

nothing; the request is unaffected

measuring a new rule against live traffic before you enforce it

CAPTCHA

yes, unless a valid unexpired token is present

a puzzle; solving it issues a token that satisfies later requests

a human-facing path where you will trade friction for stopping automation

Challenge

yes, unless a valid unexpired token is present

nothing visible — a silent browser challenge

you want to filter non-browser clients without annoying people

Nobody writes a WAF from scratch. AWS Managed Rules ship maintained, versioned groups you add by reference: the core rule set (broad OWASP-style coverage), known bad inputs, SQL database, platform groups for Linux, POSIX, Windows, PHP and WordPress, admin protection, Amazon IP reputation list, anonymous IP list, Bot Control, Account Takeover Prevention and Account Creation Fraud Prevention. Marketplace sellers publish more — that is what "third-party WAF rules" means in practice, and the integration is the same RuleGroupReferenceStatement, so your ladder can mix AWS, vendor and hand-written rules at chosen priorities.

Two mechanisms make managed groups workable. Labels are strings a rule adds to the request's evaluation context when it matches; later rules can match on a label. Managed groups label prolifically (bot categories, signal types, which sub-rule fired), so the professional pattern is: run the group with its action overridden to Count, read the labels in the logs, then write your own rules that match specific labels and take the action you want. The second mechanism is the scope-down statement — a condition that narrows what a rule group or rate-based rule even looks at, so ATP inspects only /login and the expensive body-inspection rules skip your static assets.

Rate-based rules count requests per aggregation key over a trailing window: 1, 2, 5 (the default) or 10 minutes, with a limit from 10 up to two billion. The key is what makes them precise — source IP, forwarded IP (behind a proxy, from X-Forwarded-For), or custom keys built from a header, cookie, query argument, URI path, HTTP method, label namespace or JA3 TLS fingerprint, and you can combine several. While a key is over the limit, matching requests take the rule's action; WAF re-estimates rates continuously but not instantaneously, so expect a short lag at both the start and the end of a block. Client fingerprinting lives here too: a JA3 fingerprint aggregation key rate-limits a specific TLS client stack rather than an address, which is how you reach a bot fleet that rotates IPs.

How far do managed rules get you against the OWASP Top 10?

A long way on the injection-shaped classes — SQL injection, cross-site scripting, path traversal, malicious payload patterns, bad bots and known-exploited inputs — because those are detectable in the request. They get you nowhere on broken access control, broken authentication, insecure design, or vulnerable dependencies, because those are properties of your application, not of a request. Say it plainly in a design review: a web ACL is virtual patching and attack-surface reduction, not a fix. The honest exam answer to "we are exposed to OWASP Top 10 issues" is usually both — managed rules now, code fixed next.

Logging: where the evidence goes

Full logging goes to a CloudWatch Logs log group, an S3 bucket, or Amazon Data Firehose (which is how you land it in OpenSearch, Splunk or a third-party SIEM). Names must start with aws-waf-logs-. Logs record the terminating rule, every non-terminating match, the labels, and the request itself — so you can redact fields like authorization and cookie, and filter logging down to blocked requests if volume hurts. The console also shows sampled requests from the last three hours, which is enough to confirm a rule fires but never enough to measure a false-positive rate.

OCSF and third-party integrations (new in C03)

Two separate integration stories. Inbound: third-party WAF rule groups from AWS Marketplace, referenced in your web ACL like any other group. Outbound: normalizing your edge telemetry so somebody else can read it — AWS WAFv2 logs are a natively supported Amazon Security Lake source, so enabling them lands WAF events in your lake converted to the Open Cybersecurity Schema Framework and Apache Parquet, partitioned per Region and account. Anything Security Lake does not natively collect (a vendor bot-defence feed, another CDN) comes in as a custom source you map into OCSF yourself. The point of OCSF is one query shape across AWS and non-AWS security data.

What a web ACL can be associated with

CloudFront distributions (Global scope, managed in us-east-1), and these regional resources: Application Load Balancers, API Gateway REST APIs, AppSync GraphQL APIs, Amazon Cognito user pools, App Runner services, Verified Access instances and Amplify apps. A regional web ACL must be created in the same Region as the resource. Note what is absent: Network Load Balancers, EC2 instances and raw TCP services — WAF inspects HTTP, so there must be an HTTP-aware resource to attach to.

Custom rules: statements and logic

A rule body is a statement tree. Match statements inspect a request component (URI path, query string, a single header, all headers, cookies, body, method, JA3 fingerprint) with a transformation (lowercase, URL-decode, HTML-decode, compress whitespace) and a condition (exactly, contains, starts with, regex, size, SQLi, XSS, IP set, regex pattern set, geo match, label match). Then AND, OR and NOT compose them. Text transformations matter more than people expect: %2E%2E%2F is ../, and a rule that only matches the literal string is trivially evaded.

Key terms: WAF rule group, AWS Managed Rules, Rate-based rule, Scope-down statement, WAF label, OCSF ingestion

Interactive checkpoint quiz (2 questions) — open this page in a browser to take it.