references/adaptive-mitigation-playbook-for-forwarded-signals.md
# Adaptive Mitigation Playbook for Forwarded Signals
## Overview
Domain expertise for what the application does once a bot confidence signal reaches it, the decision
layer on top of the labels-to-confidence-signal and dynamic-label-interpolation references. Covers
the graduated response by confidence level, the friction-free path for teams that will not use
CAPTCHA, and the one piece that is AWS WAF configuration: a rate-based rule aggregating on the
session token for volumetric abuse.
This reference is mostly application-side guidance, not AWS WAF steps. Deciding when to move a
customer from signal forwarding into this playbook belongs to the router and reasoning layer, not to
this reference reaching into another.
Execute commands using the AWS MCP server when connected (sandboxed execution, audit logging,
observability). Fall back to the AWS CLI otherwise.
## Table of Contents
- Overview
- Workflow
- Graduated response by confidence
- The friction-free path without CAPTCHA
- Volumetric abuse: rate-based on the session token
- Boundary
- Troubleshooting
- Procedure
- Security Considerations
- Additional Resources
## Workflow
To apply the adaptive mitigation playbook, follow the guidance below and the procedure for the one
AWS WAF piece. The application owns the low, medium, and high responses; AWS WAF owns the rate-based
rule.
## Graduated response by confidence
A single block-or-allow decision either lets abuse through or blocks real users. A graduated
response uses the confidence signal the application already receives.
**Constraints:**
- You SHOULD guide the application to a graduated response: at low confidence, withhold valuable
data or nudge the user to sign in; at medium, require authentication or step-up multi-factor
authentication (MFA); at high, route to a manual-review queue rather than auto-processing
- You MUST frame these as application-side responses the customer implements, not as AWS WAF steps
this reference executes
## The friction-free path without CAPTCHA
Teams that refuse CAPTCHA still need to slow abuse without blocking real users.
**Constraints:**
- You SHOULD offer the friction-free responses (data withholding, step-up authentication,
manual-review routing) for teams that will not use CAPTCHA, rather than falling back to blocking
## Volumetric abuse: rate-based on the session token
Volumetric abuse from sessions that each stay under a per-IP limit is keyed to a token, not an
address. This is the one piece that is AWS WAF configuration.
**Constraints:**
- You MUST add a rate-based rule aggregating on the session token or JSON Web Token (JWT) for the
volumetric case, so the limit follows the session rather than the IP (see the adding-rate-based-
rules reference for the mechanics)
- You SHOULD let the application handle the throttling response (returning fewer items, slowing the
flow) once the rate-based rule flags the session
## Boundary
This reference is the decision layer, not an orchestrator.
**Constraints:**
- You MUST NOT chain the customer from this playbook into a different workflow; the router and
reasoning layer decide when a customer moves between forwarding signals and acting on them
- You SHOULD keep this reference focused on the response to a signal that already exists, not on
producing the signal (that is the labels-to-confidence and interpolation references)
## Troubleshooting
### Real users get blocked under the playbook
The response is too aggressive at low confidence. Withhold data or nudge to sign in rather than
block (Graduated response by confidence).
### Volumetric abuse continues despite a per-IP rate limit
The abuse is per-session, not per-IP. Add a rate-based rule on the session token (Volumetric abuse:
rate-based on the session token).
## Procedure
### Overview
This procedure covers the one AWS WAF piece, the session-token rate-based rule; the graduated
responses are application-side guidance above.
### Parameters
- **web_acl_name**, **web_acl_id**, **scope** (required): Identify the web ACL.
- **session_key** (required): The session token or JWT field to aggregate on.
**Constraints for parameter acquisition:**
- You MUST ask for all required parameters upfront in a single prompt
- You MUST confirm a confidence signal is already reaching the application before advising on the
graduated responses
### Steps
#### 1. Verify dependencies
**Constraints:**
- You MUST confirm credentials with `aws sts get-caller-identity`
- You MUST confirm the session token or JWT is present on the requests to aggregate on
#### 2. Add the session-token rate-based rule
**Constraints:**
- You MUST add a rate-based rule with a `CUSTOM_KEYS` aggregation on the session token or JWT, in
Count first, following the adding-rate-based-rules reference for the window and threshold rules
- You MUST fetch the current `LockToken` with `get-web-acl` immediately before `update-web-acl`
and pass the full rule set, since `--rules` is a complete replacement
- You MUST preserve the web ACL's existing `DefaultAction` from the `get-web-acl` response and pass
it back as `{default_action}`; do not assume `Allow={}`, since that would silently open all
unmatched traffic on a web ACL whose default action is `Block`. For example, a `CUSTOM_KEYS`
rate-based rule aggregating on the session-token header, in Count:
```
aws wafv2 get-web-acl --name {web_acl_name} --scope {scope} --id {web_acl_id} --region {region}
aws wafv2 update-web-acl --name {web_acl_name} --scope {scope} --id {web_acl_id} \
--lock-token {lock_token} --default-action {default_action} \
--rules '[{"Name":"RateLimitSession","Priority":1,"Action":{"Count":{}},"Statement":{"RateBasedStatement":{"Limit":100,"EvaluationWindowSec":300,"AggregateKeyType":"CUSTOM_KEYS","CustomKeys":[{"Header":{"Name":"{session_key}","TextTransformations":[{"Priority":0,"Type":"NONE"}]}}]}},"VisibilityConfig":{"SampledRequestsEnabled":true,"CloudWatchMetricsEnabled":true,"MetricName":"RateLimitSession"}}]' \
--visibility-config SampledRequestsEnabled=true,CloudWatchMetricsEnabled=true,MetricName={web_acl_name} \
--region {region}
```
- You SHOULD leave the throttling response to the application once the rule flags the session
#### 3. Surface the console link
**Constraints:**
- You MUST present the web ACL console link and tell the customer to confirm the rate-based rule:
```
https://us-east-1.console.aws.amazon.com/wafv2/homev2/web-acls?region={region}
```
### Example
#### Example input
```json
{
"web_acl_name": "example-webacl",
"web_acl_id": "abc",
"scope": "REGIONAL",
"session_key": "x-session-token"
}
```
#### Example output
```
Application guidance: low -> withhold data / nudge sign-in; medium -> step-up MFA; high -> manual review.
Added a CUSTOM_KEYS rate-based rule on x-session-token for the volumetric case, in Count.
Open the web ACL and confirm the rate-based rule:
https://us-east-1.console.aws.amazon.com/wafv2/homev2/web-acls?region=us-east-1
```
### Troubleshooting
#### Real users blocked
Soften the low-confidence response (Step, Graduated response by confidence).
#### Per-session abuse continues
Add the session-token rate-based rule (Step 2).
## Security Considerations
This procedure modifies a security control, so misconfiguration directly weakens the application's defenses.
- **Least-privilege IAM.** You MUST grant only the specific `wafv2:` actions a task needs (for example `wafv2:GetWebACL` and `wafv2:UpdateWebACL`) rather than `wafv2:*` or the `AWSWAFFullAccess` managed policy.
- **Ephemeral credentials.** You MUST use IAM roles with temporary credentials (such as an EC2 instance profile, SSO session, or `aws sts assume-role`) rather than long-lived IAM user access keys when running these WAF CLI commands.
- **Monitor configuration changes.** You SHOULD enable AWS CloudTrail on `wafv2` management events and set CloudWatch alarms on critical web ACL configuration changes (such as `DeleteWebACL` and `UpdateWebACL` rule removals) and on the web ACL's `BlockedRequests` and `CountedRequests` metrics, so rule changes and sudden spikes in blocked or counted traffic are detected.
- **Defense in depth.** You SHOULD treat the forwarded confidence signal as one input among several, not a sole gate; over-relying on a single signal without defense in depth leaves the application exposed if the signal is evaded or degraded.
## Additional Resources
- [How to use AWS WAF Bot Control for targeted bots signals and mitigate evasive bots with adaptive user experience (AWS Networking & Content Delivery Blog)](https://aws.amazon.com/blogs/networking-and-content-delivery/how-to-use-aws-waf-bot-control-for-targeted-bots-signals-and-mitigate-evasive-bots-with-adaptive-user-experience/)
- [AWS WAF CAPTCHA and Challenge actions (AWS WAF Developer Guide)](https://docs.aws.amazon.com/waf/latest/developerguide/waf-captcha-and-challenge.html)
- [Using rate-based rule statements in AWS WAF (AWS WAF Developer Guide)](https://docs.aws.amazon.com/waf/latest/developerguide/waf-rule-statement-type-rate-based.html)
references/adding-managed-rules-and-tuning-with-count-mode.md
# Adding Managed Rules and Tuning with Count Mode
## Overview
Domain expertise for adding AWS Managed Rules rule groups to a web ACL and rolling them out without
blocking legitimate traffic. Covers matching rule groups to the workload, the web ACL capacity unit
(WCU) budget (the basic price covers up to 1,500 WCUs; a web ACL holds a hard maximum of 5,000),
the Count-mode-first tuning path, and reading the triggering rule from logs to override just that
rule rather than the whole group.
Does not cover rate-based rules, match rules, bot, or fraud rule groups; those are separate
references. Logging must already be set up (see the logging reference).
Execute commands using the AWS MCP server when connected (sandboxed execution, audit logging,
observability). Fall back to the AWS CLI otherwise.
## Table of Contents
- Overview
- Workflow
- Decision: which managed rule groups
- WCU budget: 1,500 priced tier, 5,000 hard maximum
- Count mode first
- Override one rule, not the whole group
- Troubleshooting
- Procedure
- Security Considerations
- Additional Resources
## Workflow
To add managed rules and tune them end to end, follow the procedure exactly. See the Procedure
section below.
The procedure covers:
- Selecting managed rule groups that match the workload, within the WCU budget
- Adding them in Count mode with logging and metrics on
- Reviewing which legitimate requests would have been blocked
- Overriding the offending rules and switching the rest to Block
## Decision: which managed rule groups
| Workload | Rule group |
| --- | --- |
| Broad coverage (OWASP Top 10) | Core Rule Set (CRS) |
| Known exploit patterns | Known Bad Inputs |
| Database-backed application | SQL database rule group |
| Reputation filtering | Amazon IP reputation list, Anonymous IP list |
**Constraints:**
- You MUST match rule groups to the workload using the published rule group list, rather than
enabling all of them or none
- You SHOULD start from a small baseline (Core Rule Set plus one or two targeted groups) and add
more only as needed
## WCU budget: 1,500 priced tier, 5,000 hard maximum
Each managed rule group consumes WCUs against the web ACL's capacity. The basic web ACL price
covers up to 1,500 WCUs; beyond that, usage is billed on a tiered model that AWS WAF adjusts
automatically. The hard maximum for a web ACL is 5,000 WCUs. The Core Rule Set alone is 700, so a
second large group moves into the priced tier quickly.
**Constraints:**
- You MUST track WCU usage as rule groups are added, naming the real numbers: the basic price
covers up to 1,500 WCUs and the web ACL maximum is 5,000 WCUs
- You SHOULD account for the Core Rule Set's 700 WCUs before adding a second large group such as
Known Bad Inputs or the Anonymous IP list
- You MUST NOT describe 1,500 as a ceiling or limit; it is the point where tiered pricing begins,
not a cap. Crossing 1,500 increases cost but does not block traffic or rule additions
- You SHOULD note the 5,000 WCU maximum is fixed and not raisable; when a web ACL approaches it,
trim or consolidate rules rather than expecting a quota increase
## Count mode first
Adding a managed group straight in Block mode can take down legitimate traffic, because the
predefined rules match patterns the application uses normally.
**Constraints:**
- You MUST add managed rule groups in Count mode first, which records matches without changing how
requests are handled
- You MUST switch to Block only after the customer reviews the Count-mode matches
## Override one rule, not the whole group
When a false positive appears, customers often disable the whole rule group and lose its
protection. The fix is to override only the offending rule.
**Constraints:**
- You MUST identify the triggering rule from the logs and override just that rule to Count using
`RuleActionOverrides`, rather than disabling the group
- You MUST set the group's `OverrideAction` to `None` when using individual `RuleActionOverrides`;
setting `OverrideAction` to `Count` overrides the whole group and the individual overrides have
no effect
- You SHOULD note that a rule overridden to Count still adds its labels, so a downstream label-match
rule can still act on it
## Troubleshooting
### Legitimate traffic is blocked after enabling a group
A managed rule is a false positive for this application. Find it in the logs and override just that
rule to Count (Override one rule, not the whole group).
### The web ACL hit the 5,000 WCU maximum
The combined rule groups exceed the 5,000 WCU hard maximum, which is not raisable. Trim or
consolidate rules (WCU budget: 1,500 priced tier, 5,000 hard maximum). Note: crossing 1,500 WCUs
does not cause this error; it only moves the web ACL into tiered pricing.
### Individual rule overrides have no effect
`OverrideAction` is set to `Count` for the whole group, which cancels individual overrides. Set
`OverrideAction` to `None` (Override one rule, not the whole group).
## Procedure
### Overview
This procedure adds managed rule groups in Count mode within the WCU budget, tunes false positives,
and switches to Block, then surfaces the console link.
### Parameters
- **web_acl_name**, **web_acl_id**, **scope** (required): Identify the web ACL.
- **rule_groups** (required): The managed rule groups to add, matched to the workload.
**Constraints for parameter acquisition:**
- You MUST ask for all required parameters upfront in a single prompt
- You MUST confirm logging is already enabled before adding rules in Count mode
### Steps
#### 1. Verify dependencies
**Constraints:**
- You MUST confirm credentials with `aws sts get-caller-identity`
- You MUST confirm logging and request sampling are on (see the logging reference)
#### 2. Add managed rule groups in Count mode
**Constraints:**
- You MUST add each rule group with `OverrideAction` set to `Count` initially, tracking WCU usage
(the basic price covers up to 1,500 WCUs; the web ACL maximum is 5,000)
- You MUST fetch the current `LockToken` with `get-web-acl` immediately before each `update-web-acl`
and pass the full rule set, since `--rules` is a complete replacement
- You MUST preserve the web ACL's existing `DefaultAction` from the `get-web-acl` response and pass
it back as `{default_action}`; do not assume `Allow={}`, since that would silently open all
unmatched traffic on a web ACL whose default action is `Block`:
```
aws wafv2 get-web-acl --name {web_acl_name} --scope {scope} --id {web_acl_id} --region {region}
aws wafv2 update-web-acl --name {web_acl_name} --scope {scope} --id {web_acl_id} \
--lock-token {lock_token} --default-action {default_action} \
--rules '[{"Name":"AWS-CRS","Priority":1,"Statement":{"ManagedRuleGroupStatement":{"VendorName":"AWS","Name":"AWSManagedRulesCommonRuleSet"}},"OverrideAction":{"Count":{}},"VisibilityConfig":{"SampledRequestsEnabled":true,"CloudWatchMetricsEnabled":true,"MetricName":"AWS-CRS"}}]' \
--visibility-config SampledRequestsEnabled=true,CloudWatchMetricsEnabled=true,MetricName={web_acl_name} \
--region {region}
```
#### 3. Review and tune
**Constraints:**
- You MUST review the Count-mode matches in the logs and sampled requests over a representative
period
- You MUST override only the rules that produce false positives, using `RuleActionOverrides` with
the group's `OverrideAction` set to `None`
#### 4. Switch to Block and surface the console link
**Constraints:**
- You MUST switch the tuned groups to enforce by setting `OverrideAction` to `None` and let the
group's own actions apply
- You MUST present the web ACL console link and tell the customer to open it and confirm the rules
and their actions:
```
https://us-east-1.console.aws.amazon.com/wafv2/homev2/web-acls?region={region}
```
### Example
#### Example input
```json
{
"web_acl_name": "example-webacl",
"web_acl_id": "abc",
"scope": "REGIONAL",
"rule_groups": ["AWSManagedRulesCommonRuleSet", "AWSManagedRulesKnownBadInputsRuleSet"]
}
```
#### Example output
```
Added Core Rule Set (700 WCU) and Known Bad Inputs (200 WCU) in Count mode — 900 WCUs used (within the 1,500 base-price tier; web ACL max is 5,000).
Reviewed matches, overrode CrossSiteScripting_BODY to Count for the API path false positive.
Switched the rest to Block.
Open the web ACL and confirm the rules and actions:
https://us-east-1.console.aws.amazon.com/wafv2/homev2/web-acls?region=us-east-1
```
### Troubleshooting
#### Legitimate traffic blocked
Find the offending rule in the logs and override just it to Count (Step 3).
#### Capacity maximum hit
The groups exceed the 5,000 WCU maximum (not 1,500, which is only a pricing threshold). Trim or consolidate rules (Step 2).
## Security Considerations
This procedure modifies a security control, so misconfiguration directly weakens the application's defenses.
- **Least-privilege IAM.** You MUST grant only the specific `wafv2:` actions a task needs (for example `wafv2:GetWebACL` and `wafv2:UpdateWebACL`) rather than `wafv2:*` or the `AWSWAFFullAccess` managed policy.
- **Ephemeral credentials.** You MUST use IAM roles with temporary credentials (such as an EC2 instance profile, SSO session, or `aws sts assume-role`) rather than long-lived IAM user access keys when running these WAF CLI commands.
- **Monitor configuration changes.** You SHOULD enable AWS CloudTrail on `wafv2` management events and set CloudWatch alarms on critical web ACL configuration changes (such as `DeleteWebACL` and `UpdateWebACL` rule removals) and on the web ACL's `BlockedRequests` and `CountedRequests` metrics, so rule changes and sudden spikes in blocked or counted traffic are detected.
## Additional Resources
- [Using managed rule groups in AWS WAF (AWS WAF Developer Guide)](https://docs.aws.amazon.com/waf/latest/developerguide/waf-managed-rule-groups.html)
- [AWS Managed Rules rule groups list (AWS WAF Developer Guide)](https://docs.aws.amazon.com/waf/latest/developerguide/aws-managed-rule-groups-list.html)
- [Baseline rule groups (AWS WAF Developer Guide)](https://docs.aws.amazon.com/waf/latest/developerguide/aws-managed-rule-groups-baseline.html)
- [Testing and tuning your AWS WAF protections (AWS WAF Developer Guide)](https://docs.aws.amazon.com/waf/latest/developerguide/web-acl-testing.html)
references/adding-rate-based-rules.md
# Adding Rate-Based Rules
## Overview
Domain expertise for throttling HTTP floods and brute force with AWS WAF rate-based rules. Covers
the aggregation key choice, the request floor and the allowed evaluation windows, the small cap on
rate-based rules per web ACL and the composite-key way around rule sprawl, scope-down to limit a
subset of requests, and the Count-mode-first path that also matters for Shield cost-protection
eligibility.
Does not cover managed rules, match rules, bot, or fraud rule groups; those are separate
references. Account-based abuse (credential stuffing, fake accounts) goes to the fraud control
reference, not here.
Execute commands using the AWS MCP server when connected (sandboxed execution, audit logging,
observability). Fall back to the AWS CLI otherwise.
## Table of Contents
- Overview
- Workflow
- Decision: aggregation key
- Request floor and evaluation windows
- The per-web-ACL cap and composite keys
- Scope-down to a subset of requests
- Count mode and the Shield cost-protection link
- Troubleshooting
- Procedure
- Security Considerations
- Additional Resources
## Workflow
To add a rate-based rule end to end, follow the procedure exactly. See the Procedure section below.
The procedure covers:
- Choosing the aggregation key and a measured threshold
- Setting a valid evaluation window
- Adding a scope-down statement to target a subset of requests
- Running in Count mode, confirming the threshold, then switching to Block
## Decision: aggregation key
| Key | Use when | Avoid when |
| --- | --- | --- |
| `IP` | Direct client connections, no proxy or CDN | Behind a CDN; all requests collapse to the CDN IP |
| `FORWARDED_IP` | A trusted proxy forwards the real client IP in a header | The header is absent or attacker-controlled |
| `CUSTOM_KEYS` | Per-user or per-tenant limits (API key, session, user ID); up to 5 components | The key field is absent on many requests |
| `CONSTANT` | A hard ceiling on total requests to a path; always requires scope-down | Per-client limits are needed |
**Constraints:**
- You MUST choose `FORWARDED_IP` (not `IP`) when the application sits behind a CDN, or the limit
acts on the CDN address (see the recovering-the-real-client-IP reference)
- You MUST add a scope-down statement when using `CONSTANT`; the API rejects it otherwise
## Request floor and evaluation windows
Customers assume an arbitrary threshold and window and get surprised by the constraints.
**Constraints:**
- You MUST keep the request limit at or above the floor of 10 requests
- You MUST set the evaluation window to one of 60, 120, 300, or 600 seconds; no other value is
valid
- You SHOULD compute the limit as acceptable requests per second times the window in seconds
## The per-web-ACL cap and composite keys
A web ACL allows only a small number of these high-cost rules (roughly ten). Customers building one
rate-based rule per URI path hit the cap.
**Constraints:**
- You MUST NOT add a separate rate-based rule per path because this quickly exhausts the cap; use
composite aggregation keys plus scope-down inside fewer rules
- You SHOULD solve per-path limiting with `CUSTOM_KEYS` (for example IP plus API key) and a
scope-down statement, rather than rule sprawl
## Scope-down to a subset of requests
Customers expect a rate-based rule to count only a specific path, then find it counting all
traffic, because they did not scope it down.
**Constraints:**
- You MUST add a scope-down statement when the customer wants to rate limit a subset such as a
login endpoint
- You SHOULD apply a `LOWERCASE` text transformation on the path match so casing does not cause the
scope-down to miss
## Count mode and the Shield cost-protection link
Turning a new rate-based rule straight to Block catches legitimate bursts. Leaving it in Count has
a second consequence on CloudFront and Application Load Balancer resources.
**Constraints:**
- You MUST run the rule in Count mode against production traffic to confirm the threshold before
switching to Block
- You MUST flag that on CloudFront and Application Load Balancer resources, a rate-based rule in
Block mode is a prerequisite for AWS Shield Advanced cost protection credits; a Count-only rule
silently disqualifies a future claim (the shieldadvanced skill owns that workflow)
## Troubleshooting
### The rule never fires under load
The scope-down is not matching (often a casing issue), or the threshold is too high. Add
`LOWERCASE`, or remove the scope-down to test (Scope-down to a subset of requests).
### Rule creation fails validation
`CONSTANT` was used without a scope-down statement, or the window is not one of the allowed values
(Request floor and evaluation windows).
### The rule fires for some sources but not others
Traffic arrives via a proxy and the rule reads the proxy IP. Switch to `FORWARDED_IP` (Decision:
aggregation key).
## Procedure
### Overview
This procedure adds a rate-based rule with a measured threshold, a valid window, and a scope-down,
runs it in Count, then switches to Block, and surfaces the console link.
### Parameters
- **web_acl_name**, **web_acl_id**, **scope** (required): Identify the web ACL.
- **aggregation_key** (required): `IP`, `FORWARDED_IP`, `CUSTOM_KEYS`, or `CONSTANT`.
- **custom_key_components** (required when `aggregation_key` is `CUSTOM_KEYS`): Up to 5 key
components (for example `IP`, a header name such as `x-api-key`) used for composite aggregation.
- **limit** (required): The request limit, at or above 10.
- **window** (required): One of 60, 120, 300, or 600 seconds.
- **scope_down** (required for `CONSTANT`, recommended otherwise): The statement narrowing which
requests are counted.
**Constraints for parameter acquisition:**
- You MUST ask for all required parameters upfront in a single prompt
- You MUST confirm logging is enabled so the Count-mode threshold can be validated
### Steps
#### 1. Verify dependencies
**Constraints:**
- You MUST confirm credentials with `aws sts get-caller-identity`
- You MUST confirm logging and sampling are on
#### 2. Add the rate-based rule in Count mode
**Constraints:**
- You MUST add the rule with a valid window and a limit at or above 10, in Count mode
- You MUST fetch the current `LockToken` with `get-web-acl` immediately before `update-web-acl` and
pass the full rule set
- You MUST preserve the web ACL's existing `DefaultAction` from the `get-web-acl` response and pass
it back as `{default_action}`; do not assume `Allow={}`, since that would silently open all
unmatched traffic on a web ACL whose default action is `Block`:
```
aws wafv2 get-web-acl --name {web_acl_name} --scope {scope} --id {web_acl_id} --region {region}
aws wafv2 update-web-acl --name {web_acl_name} --scope {scope} --id {web_acl_id} \
--lock-token {lock_token} --default-action {default_action} \
--rules '[{"Name":"RateLimit","Priority":1,"Action":{"Count":{}},"Statement":{"RateBasedStatement":{"Limit":{limit},"EvaluationWindowSec":{window},"AggregateKeyType":"CUSTOM_KEYS","CustomKeys":[{"Header":{"Name":"x-api-key","TextTransformations":[{"Priority":0,"Type":"NONE"}]}},{"IP":{}}]}},"VisibilityConfig":{"SampledRequestsEnabled":true,"CloudWatchMetricsEnabled":true,"MetricName":"RateLimit"}}]' \
--visibility-config SampledRequestsEnabled=true,CloudWatchMetricsEnabled=true,MetricName={web_acl_name} \
--region {region}
```
- You MUST include a scope-down statement for `CONSTANT`, and use composite `CUSTOM_KEYS` rather
than many per-path rules
#### 3. Confirm the threshold and switch to Block
**Constraints:**
- You MUST review Count-mode data to confirm the threshold does not catch legitimate bursts
- You MUST switch the rule action to Block once validated by re-running `update-web-acl` with the
rule's `"Action"` changed from `{"Count":{}}` to `{"Block":{}}` (fetch a fresh `LockToken` first
and pass the full rule set)
- For CloudFront and Application Load Balancer resources, you MUST confirm the rule is in Block mode
if the customer relies on Shield Advanced cost protection
#### 4. Surface the console link
**Constraints:**
- You MUST present the web ACL console link and tell the customer to open it and confirm the
rate-based rule and its action:
```
https://us-east-1.console.aws.amazon.com/wafv2/homev2/web-acls?region={region}
```
### Example
#### Example input
```json
{
"web_acl_name": "example-webacl",
"web_acl_id": "abc",
"scope": "REGIONAL",
"aggregation_key": "CUSTOM_KEYS",
"custom_key_components": ["IP", "x-api-key"],
"limit": 20,
"window": 300,
"scope_down": "URI path starts with /login"
}
```
#### Example output
```
Added a CUSTOM_KEYS (IP + x-api-key) rate-based rule, limit 20 per 300s, scoped to /login, in Count.
Confirmed the threshold does not catch legitimate logins, switched to Block.
Open the web ACL and confirm the rate-based rule and action:
https://us-east-1.console.aws.amazon.com/wafv2/homev2/web-acls?region=us-east-1
```
### Troubleshooting
#### The rule never fires
Scope-down casing or threshold too high. Add `LOWERCASE` or lower the limit (Step 2).
#### Validation error on creation
`CONSTANT` without scope-down, or an invalid window. Fix both (Step 2).
## Security Considerations
This procedure modifies a security control, so misconfiguration directly weakens the application's defenses.
- **Least-privilege IAM.** You MUST grant only the specific `wafv2:` actions a task needs (for example `wafv2:GetWebACL` and `wafv2:UpdateWebACL`) rather than `wafv2:*` or the `AWSWAFFullAccess` managed policy.
- **Ephemeral credentials.** You MUST use IAM roles with temporary credentials (such as an EC2 instance profile, SSO session, or `aws sts assume-role`) rather than long-lived IAM user access keys when running these WAF CLI commands.
- **Monitor configuration changes.** You SHOULD enable AWS CloudTrail on `wafv2` management events and set CloudWatch alarms on critical web ACL configuration changes (such as `DeleteWebACL` and `UpdateWebACL` rule removals) and on the web ACL's `BlockedRequests` and `CountedRequests` metrics, so rule changes and sudden spikes in blocked or counted traffic are detected.
## Additional Resources
- [Using rate-based rule statements in AWS WAF (AWS WAF Developer Guide)](https://docs.aws.amazon.com/waf/latest/developerguide/waf-rule-statement-type-rate-based.html)
- [Testing and tuning your AWS WAF protections (AWS WAF Developer Guide)](https://docs.aws.amazon.com/waf/latest/developerguide/web-acl-testing.html)
- [The three most important AWS WAF rate-based rules (AWS Security Blog)](https://aws.amazon.com/blogs/security/three-most-important-aws-waf-rate-based-rules/)
references/creating-a-web-acl-and-associating-it-with-a-resource.md
# Creating a Web ACL and Associating It with a Resource
## Overview
Domain expertise for putting AWS WAF in front of an application: creating a web access control list
(web ACL) and associating it with the resource it protects. Covers the immutable scope choice
(`CLOUDFRONT` in `us-east-1` versus `REGIONAL` in the resource's Region), the fact that a web ACL
filters nothing until it is associated, the one-web-ACL-per-resource and CloudFront-only
constraints, and starting rules in Count mode.
Does not cover the rules that go inside the web ACL (managed rules, rate-based, match, bot, fraud);
those are separate references. CloudFront distribution and Application Load Balancer configuration
are their own skills.
Execute commands using the AWS MCP server when connected (sandboxed execution, audit logging,
observability). Fall back to the AWS CLI otherwise.
## Table of Contents
- Overview
- Workflow
- Decision: scope
- Least-privilege IAM
- A web ACL filters nothing until associated
- One web ACL per resource, CloudFront is exclusive
- Start rules in Count mode
- Troubleshooting
- Procedure
- Security Considerations
- Additional Resources
## Workflow
To create a web ACL and associate it end to end, follow the procedure exactly. See the Procedure
section below.
The procedure covers:
- Choosing the scope from the resource type
- Creating the web ACL with a default action
- Associating it with the resource
- Confirming the association and surfacing the console link
## Decision: scope
| Resource | Scope | Region |
| --- | --- | --- |
| CloudFront distribution | `CLOUDFRONT` | `us-east-1` (required) |
| Application Load Balancer, API Gateway REST API, AppSync GraphQL API, Cognito user pool, App Runner, Verified Access, Amplify | `REGIONAL` | the resource's own Region |
**Constraints:**
- You MUST set the scope from the resource type before creating the web ACL; scope is immutable
after creation
- You MUST create a `CLOUDFRONT` web ACL in `us-east-1` regardless of where the distribution serves
- You MUST create a `REGIONAL` web ACL in the same Region as the resource it protects
## Least-privilege IAM
The credentials that run these commands should carry only the WAF actions the task needs, not broad
access.
**Constraints:**
- You MUST grant only the specific `wafv2:` actions these procedures use — for the entry-point
workflow that is `wafv2:CreateWebACL`, `wafv2:GetWebACL`, `wafv2:UpdateWebACL`, and
`wafv2:AssociateWebACL` — rather than `wafv2:*` or the `AWSWAFFullAccess` managed policy
- You SHOULD extend the same least-privilege approach to the other references (for example
`wafv2:PutLoggingConfiguration` for logging, `wafv2:CreateIPSet`/`wafv2:UpdateIPSet` for IP sets),
granting only what each task requires
- You MUST use IAM roles with temporary credentials (such as an EC2 instance profile, SSO session,
or `aws sts assume-role`) rather than long-lived IAM user access keys when running these commands
## A web ACL filters nothing until associated
A web ACL inspects no traffic until it is associated with a resource. Customers finish the web ACL
and assume traffic is filtered.
**Constraints:**
- You MUST treat the association as a required closing step, not optional
- You MUST confirm the resource is associated before reporting the setup complete
## One web ACL per resource, CloudFront is exclusive
Each resource can have only one web ACL, and a web ACL associated with a CloudFront distribution
cannot be associated with any other resource type. Customers design around a shared web ACL that
cannot exist.
**Constraints:**
- You MUST NOT associate more than one web ACL with a single resource
- You MUST NOT reuse a CloudFront-associated web ACL on a regional resource
- You SHOULD explain these constraints before the customer designs a shared web ACL
## Start rules in Count mode
Enabling rules straight to Block can take down legitimate traffic. The recommended path for a first
web ACL is Count, then Block after review.
**Constraints:**
- You SHOULD start new rules in Count mode and move them to Block only after the customer reviews
the matches
- You SHOULD confirm logging is set up first so Count-mode matches are reviewable
## Troubleshooting
### A CloudFront distribution does not appear in the association list
The web ACL was created in the wrong scope or Region. Recreate it as `CLOUDFRONT` scope in
`us-east-1` (Decision: scope).
### Rules are configured but traffic is not filtered
The web ACL is not associated with the resource. Associate it (A web ACL filters nothing until
associated).
### A web ACL cannot be reused across CloudFront and a regional resource
CloudFront web ACLs are exclusive and each resource takes one web ACL. Create separate web ACLs
(One web ACL per resource, CloudFront is exclusive).
## Procedure
### Overview
This procedure creates a web ACL in the correct scope, associates it with the resource, and
surfaces the console link to verify.
### Parameters
- **web_acl_name** (required): A name for the web ACL.
- **resource_arn** (required): The ARN of the resource to protect.
- **scope** (required): `CLOUDFRONT` or `REGIONAL`, derived from the resource type.
- **default_action** (required): `Allow` or `Block` when no rule matches. Set this deliberately to
match the intended posture; an `Allow` default passes any traffic not matched by a rule through
unfiltered, so prefer `Block` as the secure default and use `Allow` only when explicit blocking
rules carry the enforcement.
**Constraints for parameter acquisition:**
- You MUST ask for all required parameters upfront in a single prompt
- You MUST derive the scope and Region from the resource type, not from the customer's working
Region
### Steps
#### 1. Verify dependencies
**Constraints:**
- You MUST confirm credentials with `aws sts get-caller-identity`
- You MUST confirm the resource exists in the expected Region
#### 2. Create the web ACL
**Constraints:**
- You MUST create the web ACL in the correct scope and Region:
```
aws wafv2 create-web-acl --name {web_acl_name} --scope {scope} \
--default-action {default_action}={} \
--visibility-config SampledRequestsEnabled=true,CloudWatchMetricsEnabled=true,MetricName={web_acl_name} \
--region {region}
```
- You MUST capture the web ACL ARN and id from the response
#### 3. Associate the web ACL with the resource
**Constraints:**
- You MUST associate the web ACL with the resource ARN:
```
aws wafv2 associate-web-acl --web-acl-arn {web_acl_arn} --resource-arn {resource_arn} --region {region}
```
- You MUST NOT consider the setup complete until the association succeeds
#### 4. Confirm and surface the console link
**Constraints:**
- You MUST confirm the association and present the AWS WAF console link, telling the customer to
open it and confirm the web ACL and its associated resource:
```
https://us-east-1.console.aws.amazon.com/wafv2/homev2/web-acls?region={region}
```
### Example
#### Example input
```json
{
"web_acl_name": "example-frontend-webacl",
"resource_arn": "arn:aws:cloudfront::111122223333:distribution/EDFDVBD6EXAMPLE",
"scope": "CLOUDFRONT",
"default_action": "Block"
}
```
(`Block` is the secure default — unmatched requests are denied, and Allow rules admit the
traffic you intend. Use `"default_action": "Allow"` only when the customer explicitly wants an
allow-by-default web ACL whose rules do the blocking.)
#### Example output
```
Created CLOUDFRONT web ACL example-frontend-webacl in us-east-1 with default action Block.
Associated it with distribution EDFDVBD6EXAMPLE.
Open the AWS WAF console and confirm the web ACL and its associated resource:
https://us-east-1.console.aws.amazon.com/wafv2/homev2/web-acls?region=us-east-1
```
### Troubleshooting
#### The distribution is missing from the association list
The web ACL is in the wrong scope or Region. Recreate as `CLOUDFRONT` in `us-east-1` (Step 2).
#### Traffic is not being filtered
The web ACL is not associated. Associate it (Step 3).
## Security Considerations
This procedure modifies a security control, so misconfiguration directly weakens the application's defenses.
- **Least-privilege IAM.** You MUST grant only the specific `wafv2:` actions a task needs (for example `wafv2:GetWebACL` and `wafv2:UpdateWebACL`) rather than `wafv2:*` or the `AWSWAFFullAccess` managed policy.
- **Ephemeral credentials.** You MUST use IAM roles with temporary credentials (such as an EC2 instance profile, SSO session, or `aws sts assume-role`) rather than long-lived IAM user access keys when running these WAF CLI commands.
- **Monitor configuration changes.** You SHOULD enable AWS CloudTrail on `wafv2` management events and set CloudWatch alarms on critical web ACL configuration changes (such as `DeleteWebACL` and `UpdateWebACL` rule removals) and on the web ACL's `BlockedRequests` and `CountedRequests` metrics, so rule changes and sudden spikes in blocked or counted traffic are detected.
## Additional Resources
- [Resources that you can protect with AWS WAF (AWS WAF Developer Guide)](https://docs.aws.amazon.com/waf/latest/developerguide/how-aws-waf-works-resources.html)
- [Creating a web ACL in AWS WAF (AWS WAF Developer Guide)](https://docs.aws.amazon.com/waf/latest/developerguide/web-acl-creating.html)
- [Associating or disassociating protection with an AWS resource (AWS WAF Developer Guide)](https://docs.aws.amazon.com/waf/latest/developerguide/web-acl-associating-aws-resource.html)
references/forwarding-signals-with-dynamic-label-interpolation.md
# Forwarding Signals with Dynamic Label Interpolation
## Overview
Domain expertise for forwarding AWS WAF signals to the origin with a single rule using dynamic label
interpolation, instead of one custom rule per label. Covers the `${namespace:}` placeholder syntax
that resolves at evaluation time, the synthetic values for client IP and TLS fingerprints, the
10-placeholder-per-string limit and the fully-qualified-namespace rule, and the mandatory
inbound-header-stripping companion.
Does not cover turning Bot Control on, collapsing labels into a confidence signal (its own
reference), or the application's response. Those are separate references.
Execute commands using the AWS MCP server when connected (sandboxed execution, audit logging,
observability). Fall back to the AWS CLI otherwise.
## Table of Contents
- Overview
- Workflow
- One rule forwards a whole namespace
- Synthetic values
- Limits and the fully-qualified-namespace rule
- Strip inbound headers
- Troubleshooting
- Procedure
- Security Considerations
- Additional Resources
## Workflow
To forward signals with interpolation end to end, follow the procedure exactly. See the Procedure
section below.
The procedure covers:
- Adding one custom-header rule that interpolates a label namespace
- Adding synthetic values (client IP, TLS fingerprints, request ID) where wanted
- Staying within the placeholder limit and namespace rules
- Adding the inbound-header-stripping rule
## One rule forwards a whole namespace
The one-rule-per-label approach is unmaintainable; every new managed label means another web ACL
edit. Interpolation forwards an entire namespace in a single custom header.
**Constraints:**
- You MUST use the `${namespace:}` placeholder, which resolves at evaluation time, to forward a
whole namespace in one rule rather than adding a rule per label
- You SHOULD note a single label resolves to a terminal value, multiple labels to a comma-separated
list, and no match to an empty string
## Synthetic values
Interpolation exposes values that resolve from request context, not the label store, so the
customer does not have to reconstruct them downstream.
**Constraints:**
- You SHOULD forward `${awswaf:ip:}` (client IP), `${awswaf:ja3:}` and `${awswaf:ja4:}` (TLS
fingerprints), and `${awswaf:request_id:}` through interpolation when the origin needs them,
rather than parsing them by hand
## Limits and the fully-qualified-namespace rule
Two traps cause interpolation to not resolve as expected.
**Constraints:**
- You MUST keep to at most 10 placeholders per string value
- You MUST use the fully qualified namespace in interpolation for custom labels, even though
label-match statements accept the short name
## Strip inbound headers
The forwarded values arrive as `x-amzn-waf-*` headers, which an attacker can set inbound unless
they are stripped first.
**Constraints:**
- You MUST pair this workflow with the inbound-header-stripping rule (see
stripping-inbound-waf-headers-before-trusting-them), placed before the forwarding rule
- You MUST forward the `x-amzn-waf-*` signals only to an HTTPS-only origin, and the application MUST
validate the origin's TLS certificate, so the signals are not exposed in cleartext. You SHOULD use
AWS Certificate Manager (ACM) to provision and manage the origin's TLS certificate (for example on
an Application Load Balancer), so the certificate is validated and automatically renewed
## Troubleshooting
### A custom label does not resolve in interpolation
The short name was used. Use the fully qualified namespace (Limits and the fully-qualified-namespace
rule).
### A header value is truncated or missing placeholders
The string exceeds 10 placeholders. Split the forwarding across values (Limits and the
fully-qualified-namespace rule).
### The origin receives a spoofed forwarded value
No inbound stripping rule is in place. Add it before the forwarding rule (Strip inbound headers).
## Procedure
### Overview
This procedure adds an interpolation-based forwarding rule, strips spoofed inbound headers, and
surfaces the console link.
### Parameters
- **web_acl_name**, **web_acl_id**, **scope** (required): Identify the web ACL.
- **namespace** (required): The label namespace to forward.
- **synthetic_values** (optional): Which of client IP, JA3, JA4, request ID to include.
**Constraints for parameter acquisition:**
- You MUST ask for all required parameters upfront in a single prompt
- You MUST confirm the labeling rules that produce the namespace run before the forwarding rule
### Steps
#### 1. Verify dependencies
**Constraints:**
- You MUST confirm credentials with `aws sts get-caller-identity`
- You MUST confirm the labels to forward are actually being produced by an earlier rule or group
#### 2. Add the interpolation forwarding rule
**Constraints:**
- You MUST add a custom-header rule that interpolates the namespace with `${namespace:}`, staying
within 10 placeholders per string and using the fully qualified namespace for custom labels
- You MUST fetch the current `LockToken` before `update-web-acl` and pass the full rule set
- You MUST preserve the web ACL's existing `DefaultAction` from the `get-web-acl` response and pass
it back as `{default_action}`; do not assume `Allow={}`, since that would silently open all
unmatched traffic on a web ACL whose default action is `Block`. For example, a Count rule that
interpolates the namespace into a custom header sent to the origin:
```
aws wafv2 get-web-acl --name {web_acl_name} --scope {scope} --id {web_acl_id} --region {region}
aws wafv2 update-web-acl --name {web_acl_name} --scope {scope} --id {web_acl_id} \
--lock-token {lock_token} --default-action {default_action} \
--rules '[{"Name":"ForwardBotCategory","Priority":10,"Action":{"Count":{"CustomRequestHandling":{"InsertHeaders":[{"Name":"x-amzn-waf-bot-category","Value":"${awswaf:managed:aws:bot-control:bot:category:}"}]}}},"Statement":{"LabelMatchStatement":{"Scope":"NAMESPACE","Key":"awswaf:managed:aws:bot-control:bot:category:"}},"VisibilityConfig":{"SampledRequestsEnabled":true,"CloudWatchMetricsEnabled":true,"MetricName":"ForwardBotCategory"}}]' \
--visibility-config SampledRequestsEnabled=true,CloudWatchMetricsEnabled=true,MetricName={web_acl_name} \
--region {region}
```
#### 3. Strip inbound headers and surface the console link
**Constraints:**
- You MUST add the inbound `x-amzn-waf-*` stripping rule before the forwarding rule
- You MUST present the web ACL console link and tell the customer to confirm the rule order:
```
https://us-east-1.console.aws.amazon.com/wafv2/homev2/web-acls?region={region}
```
### Example
#### Example input
```json
{
"web_acl_name": "example-webacl",
"web_acl_id": "abc",
"scope": "REGIONAL",
"namespace": "awswaf:managed:aws:bot-control:bot:category:",
"synthetic_values": ["ip", "ja4"]
}
```
#### Example output
```
Added one rule forwarding the bot-category namespace plus ${awswaf:ip:} and ${awswaf:ja4:} to the origin.
Added an inbound x-amzn-waf-* strip rule before it.
Open the web ACL and confirm the rule order:
https://us-east-1.console.aws.amazon.com/wafv2/homev2/web-acls?region=us-east-1
```
### Troubleshooting
#### A custom label does not resolve
Use the fully qualified namespace (Step 2).
#### Placeholders missing from the header
The string exceeds 10 placeholders. Split across values (Step 2).
## Security Considerations
This procedure modifies a security control, so misconfiguration directly weakens the application's defenses.
- **Least-privilege IAM.** You MUST grant only the specific `wafv2:` actions a task needs (for example `wafv2:GetWebACL` and `wafv2:UpdateWebACL`) rather than `wafv2:*` or the `AWSWAFFullAccess` managed policy.
- **Ephemeral credentials.** You MUST use IAM roles with temporary credentials (such as an EC2 instance profile, SSO session, or `aws sts assume-role`) rather than long-lived IAM user access keys when running these WAF CLI commands.
- **Monitor configuration changes.** You SHOULD enable AWS CloudTrail on `wafv2` management events and set CloudWatch alarms on critical web ACL configuration changes (such as `DeleteWebACL` and `UpdateWebACL` rule removals) and on the web ACL's `BlockedRequests` and `CountedRequests` metrics, so rule changes and sudden spikes in blocked or counted traffic are detected.
- **Header-spoofing risk.** Any `x-amzn-waf-*` signal forwarded to the origin can be forged inbound. You MUST add the inbound-header-stripping rule whenever a signal or client IP is forwarded (see stripping-inbound-waf-headers-before-trusting-them); without it the origin trusts a spoofable value.
- **Encrypted transport.** You MUST forward the signal only to an HTTPS-only origin and the application MUST validate the origin's TLS certificate, so the signal is not exposed in cleartext. You SHOULD use AWS Certificate Manager (ACM) to provision and manage the origin's TLS certificate (for example on an Application Load Balancer), so the certificate is validated and automatically renewed.
- **Defense in depth.** You SHOULD treat the forwarded signal as one input among several, not a sole gate; over-relying on a single signal without defense in depth leaves the application exposed if the signal is evaded or degraded.
## Additional Resources
- [Dynamic label interpolation (AWS WAF Developer Guide)](https://docs.aws.amazon.com/waf/latest/developerguide/waf-dynamic-label-interpolation.html)
- [Customizing web requests and responses in AWS WAF (AWS WAF Developer Guide)](https://docs.aws.amazon.com/waf/latest/developerguide/waf-custom-request-response.html)
references/protecting-against-bots-with-bot-control.md
# Protecting Against Bots with Bot Control
## Overview
Domain expertise for the AWS WAF Bot Control on-ramp: adding the Bot Control managed rule group,
choosing Common versus Targeted, and observing in Count mode before enforcing. Covers the sharp
difference between Common and Targeted, the application integration SDK as a precondition for
Targeted, the machine learning warm-up, the verified-bot Count-override gotcha, and the added cost.
Does not cover what to do with the labels Bot Control produces; turning labels into a confidence
signal, forwarding that signal, and the application's response are three separate references that
build on this one.
Execute commands using the AWS MCP server when connected (sandboxed execution, audit logging,
observability). Fall back to the AWS CLI otherwise.
## Table of Contents
- Overview
- Workflow
- Decision: Common vs Targeted
- The SDK is a precondition for Targeted
- Machine learning warm-up
- Verified-bot Count-override gotcha
- Added cost
- Troubleshooting
- Procedure
- Security Considerations
- Additional Resources
## Workflow
To turn on Bot Control end to end, follow the procedure exactly. See the Procedure section below.
The procedure covers:
- Adding the Bot Control rule group and choosing the inspection level
- Running in Count mode to see how traffic is labeled
- Keeping verified bots passing while tuning
- Deciding which categories to block, challenge, or allow, then enforcing
This reference is the on-ramp. Turning the labels into an application decision is the
turning-bot-control-labels-into-a-confidence-signal reference and the ones after it.
## Decision: Common vs Targeted
| Level | Detects | SDK | Best for |
| --- | --- | --- | --- |
| Common | Self-identifying bots (user-agent such as `curl`, `python-requests`, declared crawlers) and known-bad IPs | Not required | Basic filtering of honest bots |
| Targeted | Adds behavioral machine learning, browser interrogation, and token session tracking | Strongly required | Login, checkout, any high-value endpoint facing evasive bots |
**Constraints:**
- You MUST push Targeted for any real or evasive bot threat; Common alone is not meaningful
protection against bots that impersonate a real browser (headless Chrome, Puppeteer, Selenium,
residential proxies)
- You MUST NOT present Targeted as an optional upgrade when the customer faces credential stuffing
or inventory hoarding on a high-value endpoint
## The SDK is a precondition for Targeted
Targeted's behavioral machine learning, browser interrogation, and token session tracking are
largely blind without the application integration SDK or its JavaScript token.
**Constraints:**
- You MUST treat the application integration SDK as a precondition for Targeted, not an optional
add-on
- You SHOULD confirm the SDK is integrated before relying on Targeted detection
## Machine learning warm-up
The `TGT_ML_*` rules need up to roughly 24 hours to establish a traffic baseline. Customers enable
Targeted, see nothing fire immediately, and assume it is broken.
**Constraints:**
- You MUST set the expectation that `TGT_ML_*` rules need up to roughly 24 hours of warm-up before
they act
- You SHOULD advise against disabling Targeted during the warm-up window
## Verified-bot Count-override gotcha
Bot Control does not block verified bots; it labels them. Overriding the whole rule group to Count
while tuning also overrides the implicit Allow for verified bots, so they fall through to the
customer's other rules.
**Constraints:**
- You MUST add an explicit Allow rule on the `awswaf:managed:aws:bot-control:bot:verified` label
when tuning the group in Count, so verified bots keep passing
- You MUST place that Allow rule at a higher priority number than the Bot Control group, so the
verified-bot label exists when the Allow rule runs
- You SHOULD rely on the verified-bot labeling rather than blanket-blocking all automated traffic
## Added cost
Bot Control incurs additional fees beyond the basic AWS WAF charges.
**Constraints:**
- You MUST state the additional cost before the customer adds the rule group, not after it appears
on the invoice
## Troubleshooting
### Evasive bots still get through on Common
Common only catches self-identifying bots and known-bad IPs. Move to Targeted with the SDK
(Decision: Common vs Targeted).
### Targeted fires nothing right after enabling
The `TGT_ML_*` rules are still warming up. Wait up to roughly 24 hours (Machine learning warm-up).
### Verified bots get blocked while tuning
Overriding the whole group to Count canceled the verified-bot Allow. Add an explicit Allow on the
verified label (Verified-bot Count-override gotcha).
## Procedure
### Overview
This procedure adds Bot Control at the chosen level in Count mode, keeps verified bots passing,
then enforces, and surfaces the console link.
### Parameters
- **web_acl_name**, **web_acl_id**, **scope** (required): Identify the web ACL.
- **inspection_level** (required): `COMMON` or `TARGETED`.
**Constraints for parameter acquisition:**
- You MUST ask for all required parameters upfront in a single prompt
- You MUST confirm logging is enabled and, for Targeted, that the application integration SDK is
integrated
### Steps
#### 1. Verify dependencies
**Constraints:**
- You MUST confirm credentials with `aws sts get-caller-identity`
- You MUST confirm logging and sampling are on
- For Targeted, you MUST confirm the application integration SDK is in place
#### 2. Add Bot Control in Count mode
**Constraints:**
- You MUST add the Bot Control rule group at the chosen inspection level with the group in Count
while observing
- You MUST add an explicit Allow rule on `awswaf:managed:aws:bot-control:bot:verified` so verified
bots keep passing during tuning
- You MUST fetch the current `LockToken` before `update-web-acl` and pass the full rule set
- You MUST preserve the web ACL's existing `DefaultAction` from the `get-web-acl` response and pass
it back as `{default_action}`; do not assume `Allow={}`, since that would silently open all
unmatched traffic on a web ACL whose default action is `Block`. For example, the Bot Control group
at the chosen inspection level plus the verified-bot Allow rule:
```
aws wafv2 get-web-acl --name {web_acl_name} --scope {scope} --id {web_acl_id} --region {region}
aws wafv2 update-web-acl --name {web_acl_name} --scope {scope} --id {web_acl_id} \
--lock-token {lock_token} --default-action {default_action} \
--rules '[{"Name":"AWS-BotControl","Priority":1,"Statement":{"ManagedRuleGroupStatement":{"VendorName":"AWS","Name":"AWSManagedRulesBotControlRuleSet","ManagedRuleGroupConfigs":[{"AWSManagedRulesBotControlRuleSet":{"InspectionLevel":"{inspection_level}"}}]}},"OverrideAction":{"Count":{}},"VisibilityConfig":{"SampledRequestsEnabled":true,"CloudWatchMetricsEnabled":true,"MetricName":"AWS-BotControl"}},{"Name":"AllowVerifiedBots","Priority":2,"Action":{"Allow":{}},"Statement":{"LabelMatchStatement":{"Scope":"LABEL","Key":"awswaf:managed:aws:bot-control:bot:verified"}},"VisibilityConfig":{"SampledRequestsEnabled":true,"CloudWatchMetricsEnabled":true,"MetricName":"AllowVerifiedBots"}}]' \
--visibility-config SampledRequestsEnabled=true,CloudWatchMetricsEnabled=true,MetricName={web_acl_name} \
--region {region}
```
#### 3. Review and enforce
**Constraints:**
- You MUST review the labeled traffic (allowing for the Targeted warm-up) before enforcing
- You MUST decide per category whether to block, challenge, or allow, then enforce
#### 4. Surface the console link
**Constraints:**
- You MUST present the web ACL console link and tell the customer to open the Bot Control rule and
confirm its level and actions:
```
https://us-east-1.console.aws.amazon.com/wafv2/homev2/web-acls?region={region}
```
### Example
#### Example input
```json
{
"web_acl_name": "example-webacl",
"web_acl_id": "abc",
"scope": "REGIONAL",
"inspection_level": "TARGETED"
}
```
#### Example output
```
Confirmed the SDK is integrated. Added Bot Control TARGETED in Count, with an explicit Allow on the verified-bot label.
TGT_ML rules need up to ~24h to warm up before acting.
Open the web ACL and confirm the Bot Control level and actions:
https://us-east-1.console.aws.amazon.com/wafv2/homev2/web-acls?region=us-east-1
```
### Troubleshooting
#### Evasive bots get through on Common
Move to Targeted with the SDK (Step 1).
#### Nothing fires right after enabling Targeted
The machine learning is warming up; wait up to roughly 24 hours (Step 3).
#### Verified bots blocked while tuning
Add the explicit Allow on the verified label (Step 2).
## Security Considerations
This procedure modifies a security control, so misconfiguration directly weakens the application's defenses.
- **Least-privilege IAM.** You MUST grant only the specific `wafv2:` actions a task needs (for example `wafv2:GetWebACL` and `wafv2:UpdateWebACL`) rather than `wafv2:*` or the `AWSWAFFullAccess` managed policy.
- **Ephemeral credentials.** You MUST use IAM roles with temporary credentials (such as an EC2 instance profile, SSO session, or `aws sts assume-role`) rather than long-lived IAM user access keys when running these WAF CLI commands.
- **Monitor configuration changes.** You SHOULD enable AWS CloudTrail on `wafv2` management events and set CloudWatch alarms on critical web ACL configuration changes (such as `DeleteWebACL` and `UpdateWebACL` rule removals) and on the web ACL's `BlockedRequests` and `CountedRequests` metrics, so rule changes and sudden spikes in blocked or counted traffic are detected.
## Additional Resources
- [AWS WAF Bot Control rule group (AWS WAF Developer Guide)](https://docs.aws.amazon.com/waf/latest/developerguide/aws-managed-rule-groups-bot.html)
- [Adding the AWS WAF Bot Control managed rule group to your web ACL (AWS WAF Developer Guide)](https://docs.aws.amazon.com/waf/latest/developerguide/waf-bot-control-rg-using.html)
- [Using managed rule groups in AWS WAF (AWS WAF Developer Guide)](https://docs.aws.amazon.com/waf/latest/developerguide/waf-managed-rule-groups.html)
references/protecting-logins-and-signups-with-fraud-control.md
# Protecting Logins and Signups with Fraud Control
## Overview
Domain expertise for AWS WAF Fraud Control: the Account Takeover Prevention (ATP) managed rule group
for login protection and the Account Creation Fraud Prevention (ACFP) managed rule group for signup
protection. Covers why rate limiting misses this abuse, the mandatory application integration SDK,
the CloudFront-only limitation on response inspection and its workaround, and the Count-first tuning
path.
Does not cover generic rate limiting (the rate-based reference) or bot detection (the bot
references). Those are separate.
Execute commands using the AWS MCP server when connected (sandboxed execution, audit logging,
observability). Fall back to the AWS CLI otherwise.
## Table of Contents
- Overview
- Workflow
- Decision: ATP, ACFP, or both
- The SDK is mandatory
- Response inspection is CloudFront only
- Count mode first
- Troubleshooting
- Procedure
- Security Considerations
- Additional Resources
## Workflow
To protect logins and signups end to end, follow the procedure exactly. See the Procedure section
below.
The procedure covers:
- Adding ATP for login protection, ACFP for signup protection, or both
- Integrating the application integration SDK
- Configuring request inspection (and response inspection where supported)
- Running in Count mode, then mapping labels to actions
## Decision: ATP, ACFP, or both
| Threat | Rule group |
| --- | --- |
| Credential stuffing against a login page | Account Takeover Prevention (ATP) |
| Fake-account creation against a signup page | Account Creation Fraud Prevention (ACFP) |
| Both | Add both rule groups |
**Constraints:**
- You MUST reach for ATP and ACFP for account-based abuse rather than rate-based rules; the abuse is
distributed and low-rate per source, so rate limiting misses it
- You SHOULD configure the login path for ATP and both the registration page and creation paths for
ACFP
## The SDK is mandatory
Both rule groups rely on session tokens that the application integration SDK issues. Without it the
protection is weak.
**Constraints:**
- You MUST treat the application integration SDK as mandatory for ATP and ACFP, not optional
- You SHOULD confirm the SDK is integrated before relying on these rule groups
## Response inspection is CloudFront only
Response inspection (tracking login success and failure) is available only on web ACLs protecting
CloudFront distributions.
**Constraints:**
- You MUST tell the customer that on an Application Load Balancer the response-based rules such as
`VolumetricIpFailedLoginResponseHigh` and `VolumetricSessionFailedLoginResponseHigh` will not fire
- You SHOULD offer the workaround of putting CloudFront in front of the Application Load Balancer
when response inspection is needed
## Count mode first
Turning these rule groups straight to Block risks locking out real users on a busy login page.
**Constraints:**
- You MUST run ATP and ACFP in Count mode first, review the labeled traffic, then map labels to
actions before enforcing
## Troubleshooting
### Credential stuffing gets through despite a rate limit
Rate limiting misses distributed, low-rate account abuse. Add ATP (Decision: ATP, ACFP, or both).
### Response-based rules never fire on an ALB
Response inspection is CloudFront only. Put CloudFront in front of the ALB (Response inspection is
CloudFront only).
### Protection is weak even with ATP enabled
The SDK is not integrated. Integrate it (The SDK is mandatory).
## Procedure
### Overview
This procedure adds ATP, ACFP, or both with the SDK, configures inspection, runs in Count, then
maps labels to actions, and surfaces the console link.
### Parameters
- **web_acl_name**, **web_acl_id**, **scope** (required): Identify the web ACL.
- **rule_groups** (required): `ATP`, `ACFP`, or both.
- **login_path** (required for ATP): The login endpoint path.
- **registration_path** and **creation_path** (required for ACFP): The signup paths.
**Constraints for parameter acquisition:**
- You MUST ask for all required parameters upfront in a single prompt
- You MUST confirm the application integration SDK is integrated and logging is on
### Steps
#### 1. Verify dependencies
**Constraints:**
- You MUST confirm credentials with `aws sts get-caller-identity`
- You MUST confirm the SDK is integrated and logging and sampling are on
- You SHOULD confirm whether the resource is CloudFront (response inspection) or an ALB (request
inspection only)
#### 2. Add the rule groups in Count mode
**Constraints:**
- You MUST add ATP with the login path, and ACFP with both the registration and creation paths,
configuring request inspection for the username, password, and (ACFP) email fields
- You MUST configure response inspection only on a CloudFront web ACL
- You MUST fetch the current `LockToken` before `update-web-acl` and pass the full rule set
- You MUST preserve the web ACL's existing `DefaultAction` from the `get-web-acl` response and pass
it back as `{default_action}`; do not assume `Allow={}`, since that would silently open all
unmatched traffic on a web ACL whose default action is `Block`. For example, adding ATP with
request inspection on the login path:
```
aws wafv2 get-web-acl --name {web_acl_name} --scope {scope} --id {web_acl_id} --region {region}
aws wafv2 update-web-acl --name {web_acl_name} --scope {scope} --id {web_acl_id} \
--lock-token {lock_token} --default-action {default_action} \
--rules '[{"Name":"AWS-ATP","Priority":1,"Statement":{"ManagedRuleGroupStatement":{"VendorName":"AWS","Name":"AWSManagedRulesATPRuleSet","ManagedRuleGroupConfigs":[{"AWSManagedRulesATPRuleSet":{"LoginPath":"{login_path}","RequestInspection":{"PayloadType":"JSON","UsernameField":{"Identifier":"/username"},"PasswordField":{"Identifier":"/password"}}}}]}},"OverrideAction":{"Count":{}},"VisibilityConfig":{"SampledRequestsEnabled":true,"CloudWatchMetricsEnabled":true,"MetricName":"AWS-ATP"}}]' \
--visibility-config SampledRequestsEnabled=true,CloudWatchMetricsEnabled=true,MetricName={web_acl_name} \
--region {region}
```
ACFP uses `AWSManagedRulesACFPRuleSet` with `RegistrationPagePath`, `CreationPath`, and its own
`RequestInspection` (including the email field); add a `ResponseInspection` block only on a
CloudFront web ACL
#### 3. Review and map labels to actions
**Constraints:**
- You MUST review the Count-mode labels (such as the compromised-credential label) before enforcing
- You MUST map labels to actions and switch to enforcement once validated
#### 4. Surface the console link
**Constraints:**
- You MUST present the web ACL console link and tell the customer to confirm the Fraud Control rules
and their actions:
```
https://us-east-1.console.aws.amazon.com/wafv2/homev2/web-acls?region={region}
```
### Example
#### Example input
```json
{
"web_acl_name": "example-cf-webacl",
"web_acl_id": "abc",
"scope": "CLOUDFRONT",
"rule_groups": ["ATP"],
"login_path": "/api/login"
}
```
#### Example output
```
Confirmed the SDK is integrated and the resource is CloudFront, so response inspection is available.
Added ATP on /api/login with request and response inspection, in Count.
Reviewed labels, mapped the compromised-credential label to Block, then enforced.
Open the web ACL and confirm the Fraud Control rules:
https://us-east-1.console.aws.amazon.com/wafv2/homev2/web-acls?region=us-east-1
```
### Troubleshooting
#### Credential stuffing gets through
Rate limiting misses it. Add ATP (Step 2).
#### Response rules never fire on an ALB
Response inspection is CloudFront only. Front the ALB with CloudFront (Step 1).
## Security Considerations
This procedure modifies a security control, so misconfiguration directly weakens the application's defenses.
- **Least-privilege IAM.** You MUST grant only the specific `wafv2:` actions a task needs (for example `wafv2:GetWebACL` and `wafv2:UpdateWebACL`) rather than `wafv2:*` or the `AWSWAFFullAccess` managed policy.
- **Ephemeral credentials.** You MUST use IAM roles with temporary credentials (such as an EC2 instance profile, SSO session, or `aws sts assume-role`) rather than long-lived IAM user access keys when running these WAF CLI commands.
- **Monitor configuration changes.** You SHOULD enable AWS CloudTrail on `wafv2` management events and set CloudWatch alarms on critical web ACL configuration changes (such as `DeleteWebACL` and `UpdateWebACL` rule removals) and on the web ACL's `BlockedRequests` and `CountedRequests` metrics, so rule changes and sudden spikes in blocked or counted traffic are detected.
## Additional Resources
- [AWS WAF Fraud Control account takeover prevention (ATP) rule group (AWS WAF Developer Guide)](https://docs.aws.amazon.com/waf/latest/developerguide/aws-managed-rule-groups-atp.html)
- [AWS WAF Fraud Control account creation fraud prevention (ACFP) rule group (AWS WAF Developer Guide)](https://docs.aws.amazon.com/waf/latest/developerguide/aws-managed-rule-groups-acfp.html)
- [ATP example: Response inspection configuration (AWS WAF Developer Guide)](https://docs.aws.amazon.com/waf/latest/developerguide/waf-atp-control-example-response-inspection.html)
- [Testing and tuning your AWS WAF protections (AWS WAF Developer Guide)](https://docs.aws.amazon.com/waf/latest/developerguide/web-acl-testing.html)
references/recovering-the-real-client-ip-behind-a-cdn.md
# Recovering the Real Client IP Behind a CDN
## Overview
Domain expertise for making AWS WAF act on the real client IP when it sits behind a third-party
content delivery network (CDN) or proxy, where the connection IP is the CDN's. Covers enabling
forwarded-IP configuration on rate-based, IP set, and geographic match rules, forwarding
`${awswaf:ip:}` to the origin, and trusting forwarded headers only from a known upstream.
Does not cover the rules themselves in depth (see the rate-based and IP/geo references); this is the
forwarded-IP concern that cuts across them. Pairs with the inbound-header-stripping reference.
Execute commands using the AWS MCP server when connected (sandboxed execution, audit logging,
observability). Fall back to the AWS CLI otherwise.
## Table of Contents
- Overview
- Workflow
- The problem: WAF sees the CDN's IP
- Enable forwarded-IP on the affected rules
- Trust the header only from a known upstream
- Troubleshooting
- Procedure
- Security Considerations
- Additional Resources
## Workflow
To recover the real client IP end to end, follow the procedure exactly. See the Procedure section
below.
The procedure covers:
- Identifying which rules act on the wrong address
- Enabling forwarded-IP configuration on rate-based and IP or geo rules
- Optionally forwarding `${awswaf:ip:}` to the origin
- Pairing with the inbound-header-stripping rule
## The problem: WAF sees the CDN's IP
Behind a CDN, the connection IP AWS WAF reads is the CDN's, not the user's, so IP set rules,
rate-based rules, and geographic match rules all act on the wrong address. Customers often do not
know AWS WAF can recover the true client IP and conclude their rules are broken.
**Constraints:**
- You MUST recognize that IP-based rules act on the CDN address by default when behind a CDN, and
surface the forwarded-IP option rather than letting the customer disable rules that look broken
## Enable forwarded-IP on the affected rules
Forwarded-IP configuration tells the rule to read the client address from a header such as
`X-Forwarded-For`, `True-Client-IP`, or a custom header.
**Constraints:**
- You MUST enable forwarded-IP configuration on the rate-based and IP or geo rules that need the
real client address
- You SHOULD forward `${awswaf:ip:}` (the resolved client IP) to the origin via interpolation when
the origin needs it, rather than parsing the forwarding header downstream
- You MUST forward the interpolated client IP only to an HTTPS-only origin, and the application MUST
validate the origin's TLS certificate, so the header is not exposed in cleartext. You SHOULD use
AWS Certificate Manager (ACM) to provision and manage the origin's TLS certificate (for example on
an Application Load Balancer), so the certificate is validated and automatically renewed
## Trust the header only from a known upstream
A forwarding header can be set by anyone unless the upstream is trusted, which lets an attacker
forge the client address.
**Constraints:**
- You MUST trust the forwarded header only when it comes from a known, trusted upstream
- You MUST pair this with the inbound-header-stripping rule (see
stripping-inbound-waf-headers-before-trusting-them) when forwarding the client IP to the origin
## Troubleshooting
### Rate or geo rules act on the wrong source behind a CDN
The rules read the CDN IP. Enable forwarded-IP configuration on them (Enable forwarded-IP on the
affected rules).
### An attacker forges the client IP
The forwarding header is trusted from an untrusted source. Trust it only from a known upstream and
add the strip rule (Trust the header only from a known upstream).
## Procedure
### Overview
This procedure enables forwarded-IP on the affected rules, optionally forwards the client IP to the
origin, and surfaces the console link.
### Parameters
- **web_acl_name**, **web_acl_id**, **scope** (required): Identify the web ACL.
- **forwarding_header** (required): `X-Forwarded-For`, `True-Client-IP`, or a custom header the
trusted upstream sets.
- **affected_rules** (required): Which rate-based, IP set, or geo rules need the real client IP.
**Constraints for parameter acquisition:**
- You MUST ask for all required parameters upfront in a single prompt
- You MUST confirm which upstream sets the forwarding header and that it is trusted
### Steps
#### 1. Verify dependencies
**Constraints:**
- You MUST confirm credentials with `aws sts get-caller-identity`
- You MUST confirm the trusted upstream reliably sets the forwarding header
#### 2. Enable forwarded-IP on the rules
**Constraints:**
- You MUST add forwarded-IP configuration (header name and fallback behavior) to the affected
rate-based and IP or geo rules
- You MUST fetch the current `LockToken` before `update-web-acl` and pass the full rule set
- You MUST preserve the web ACL's existing `DefaultAction` from the `get-web-acl` response and pass
it back as `{default_action}`; do not assume `Allow={}`, since that would silently open all
unmatched traffic on a web ACL whose default action is `Block`. For example, a geo-match rule
reading the client IP from a forwarding header:
```
aws wafv2 get-web-acl --name {web_acl_name} --scope {scope} --id {web_acl_id} --region {region}
aws wafv2 update-web-acl --name {web_acl_name} --scope {scope} --id {web_acl_id} \
--lock-token {lock_token} --default-action {default_action} \
--rules '[{"Name":"BlockHighRiskCountries","Priority":1,"Action":{"Block":{}},"Statement":{"GeoMatchStatement":{"CountryCodes":["KP","IR"],"ForwardedIPConfig":{"HeaderName":"{forwarding_header}","FallbackBehavior":"NO_MATCH"}}},"VisibilityConfig":{"SampledRequestsEnabled":true,"CloudWatchMetricsEnabled":true,"MetricName":"BlockHighRiskCountries"}}]' \
--visibility-config SampledRequestsEnabled=true,CloudWatchMetricsEnabled=true,MetricName={web_acl_name} \
--region {region}
```
Rate-based rules take the same `ForwardedIPConfig` under `RateBasedStatement`; `FallbackBehavior`
is `MATCH` or `NO_MATCH` for when the header is absent
#### 3. Optionally forward the client IP and add the strip rule
**Constraints:**
- You SHOULD forward `${awswaf:ip:}` to the origin if it needs the client IP
- You MUST add the inbound-header-stripping rule when forwarding the client IP
#### 4. Surface the console link
**Constraints:**
- You MUST present the web ACL console link and tell the customer to confirm the rules read the
forwarded IP:
```
https://us-east-1.console.aws.amazon.com/wafv2/homev2/web-acls?region={region}
```
### Example
#### Example input
```json
{
"web_acl_name": "example-webacl",
"web_acl_id": "abc",
"scope": "REGIONAL",
"forwarding_header": "X-Forwarded-For",
"affected_rules": ["RateLimitLogin", "BlockHighRiskCountries"]
}
```
#### Example output
```
Enabled forwarded-IP (X-Forwarded-For) on the rate-based and geo rules so they act on the real client IP.
Confirmed the CDN is a trusted upstream and added an inbound x-amzn-waf-* strip rule.
Open the web ACL and confirm the rules read the forwarded IP:
https://us-east-1.console.aws.amazon.com/wafv2/homev2/web-acls?region=us-east-1
```
### Troubleshooting
#### Rules still act on the CDN address
Forwarded-IP is not enabled on them. Add it (Step 2).
#### The client IP can be forged
Trust the header only from a known upstream and add the strip rule (Step 3).
## Security Considerations
This procedure modifies a security control, so misconfiguration directly weakens the application's defenses.
- **Least-privilege IAM.** You MUST grant only the specific `wafv2:` actions a task needs (for example `wafv2:GetWebACL` and `wafv2:UpdateWebACL`) rather than `wafv2:*` or the `AWSWAFFullAccess` managed policy.
- **Ephemeral credentials.** You MUST use IAM roles with temporary credentials (such as an EC2 instance profile, SSO session, or `aws sts assume-role`) rather than long-lived IAM user access keys when running these WAF CLI commands.
- **Monitor configuration changes.** You SHOULD enable AWS CloudTrail on `wafv2` management events and set CloudWatch alarms on critical web ACL configuration changes (such as `DeleteWebACL` and `UpdateWebACL` rule removals) and on the web ACL's `BlockedRequests` and `CountedRequests` metrics, so rule changes and sudden spikes in blocked or counted traffic are detected.
- **Header-spoofing risk.** Any `x-amzn-waf-*` signal forwarded to the origin can be forged inbound. You MUST add the inbound-header-stripping rule whenever a signal or client IP is forwarded (see stripping-inbound-waf-headers-before-trusting-them); without it the origin trusts a spoofable value.
- **Encrypted transport.** You MUST forward the signal only to an HTTPS-only origin and the application MUST validate the origin's TLS certificate, so the signal is not exposed in cleartext. You SHOULD use AWS Certificate Manager (ACM) to provision and manage the origin's TLS certificate (for example on an Application Load Balancer), so the certificate is validated and automatically renewed.
- **Defense in depth.** You SHOULD treat the forwarded signal as one input among several, not a sole gate; over-relying on a single signal without defense in depth leaves the application exposed if the signal is evaded or degraded.
## Additional Resources
- [Using forwarded IP addresses in AWS WAF (AWS WAF Developer Guide)](https://docs.aws.amazon.com/waf/latest/developerguide/waf-rule-statement-forwarded-ip-address.html)
- [Dynamic label interpolation (AWS WAF Developer Guide)](https://docs.aws.amazon.com/waf/latest/developerguide/waf-dynamic-label-interpolation.html)
references/seeing-and-managing-ai-crawler-traffic.md
# Seeing and Managing AI and LLM Crawler Traffic
## Overview
Domain expertise for deciding, per AI scraper, whether to allow or block it in AWS WAF. Covers the
AI activity visibility surface, the AI and large language model (LLM) bot labels Bot Control
applies, category-based handling, and composing the AI labels into the existing bot confidence
signal.
Does not cover turning Bot Control on (the protecting-against-bots reference) or the confidence
signal chain in depth (its own references). This reference depends on Bot Control being enabled.
Execute commands using the AWS MCP server when connected (sandboxed execution, audit logging,
observability). Fall back to the AWS CLI otherwise.
## Table of Contents
- Overview
- Workflow
- See the AI traffic first
- Handle AI traffic by category
- Compose into the confidence signal
- Troubleshooting
- Procedure
- Security Considerations
- Additional Resources
## Workflow
To see and manage AI crawler traffic end to end, follow the procedure exactly. See the Procedure
section below.
The procedure covers:
- Using the AI activity surface to see which AI and LLM crawlers are arriving
- Reading the AI and LLM bot labels
- Handling that traffic by category (allow wanted crawlers, block or challenge unwanted ones)
- Optionally composing the AI labels into the existing confidence signal
## See the AI traffic first
Customers want to allow some AI crawlers and block others but cannot see the AI traffic, so they
cannot make the decision.
**Constraints:**
- You MUST surface the AI activity view and the AI and LLM bot labels so the customer can see which
crawlers arrive before deciding on each
- You SHOULD confirm Bot Control is enabled, since the AI labels come from it
## Handle AI traffic by category
Customers treat all AI crawler traffic as one block-or-allow decision when they want different
handling per crawler.
**Constraints:**
- You MUST handle AI traffic by category using the bot labels, so the customer can allow wanted
crawlers and block or challenge unwanted ones, rather than a single blanket decision
## Compose into the confidence signal
A customer already forwarding a bot confidence signal can feed AI labels into the same chain rather
than building a separate path.
**Constraints:**
- You SHOULD compose the AI labels into the existing confidence signal (see
turning-bot-control-labels-into-a-confidence-signal) rather than building a separate AI path
- You MUST NOT duplicate the forwarding mechanism for AI traffic when a confidence signal already
exists
## Troubleshooting
### The customer cannot see which AI crawlers are arriving
The AI activity surface or AI labels are not in view, or Bot Control is not enabled. Enable Bot
Control and use the AI activity view (See the AI traffic first).
### AI handling is all-or-nothing
The customer is making one blanket decision. Handle by category using the labels (Handle AI traffic
by category).
## Procedure
### Overview
This procedure surfaces AI crawler traffic, handles it by category, optionally composes it into the
confidence signal, and surfaces the console link.
### Parameters
- **web_acl_name**, **web_acl_id**, **scope** (required): Identify the web ACL.
- **category_handling** (required): Which AI or LLM categories to allow, block, or challenge.
**Constraints for parameter acquisition:**
- You MUST ask for all required parameters upfront in a single prompt
- You MUST confirm Bot Control is enabled and producing AI labels
### Steps
#### 1. Verify dependencies
**Constraints:**
- You MUST confirm credentials with `aws sts get-caller-identity`
- You MUST confirm Bot Control is enabled and AI labels are present
#### 2. Review AI traffic and add category handling
**Constraints:**
- You MUST review the AI activity and labels before deciding
- You MUST add label-match rules handling the AI categories per the customer's decision, fetching
the current `LockToken` before `update-web-acl` and passing the full rule set
- You MUST preserve the web ACL's existing `DefaultAction` from the `get-web-acl` response and pass
it back as `{default_action}`; do not assume `Allow={}`, since that would silently open all
unmatched traffic on a web ACL whose default action is `Block`. For example, a Block rule on an
unwanted AI crawler label:
```
aws wafv2 get-web-acl --name {web_acl_name} --scope {scope} --id {web_acl_id} --region {region}
aws wafv2 update-web-acl --name {web_acl_name} --scope {scope} --id {web_acl_id} \
--lock-token {lock_token} --default-action {default_action} \
--rules '[{"Name":"BlockUnwantedAICrawler","Priority":1,"Action":{"Block":{}},"Statement":{"LabelMatchStatement":{"Scope":"LABEL","Key":"awswaf:managed:aws:bot-control:bot:category:ai"}},"VisibilityConfig":{"SampledRequestsEnabled":true,"CloudWatchMetricsEnabled":true,"MetricName":"BlockUnwantedAICrawler"}}]' \
--visibility-config SampledRequestsEnabled=true,CloudWatchMetricsEnabled=true,MetricName={web_acl_name} \
--region {region}
```
#### 3. Optionally compose into the confidence signal and surface the console link
**Constraints:**
- You SHOULD feed the AI labels into the existing confidence signal rather than a separate path
- You MUST present the web ACL console link and tell the customer to confirm the AI handling rules:
```
https://us-east-1.console.aws.amazon.com/wafv2/homev2/web-acls?region={region}
```
### Example
#### Example input
```json
{
"web_acl_name": "example-webacl",
"web_acl_id": "abc",
"scope": "REGIONAL",
"category_handling": {"allow": ["wanted search AI"], "block": ["unwanted scraper AI"]}
}
```
#### Example output
```
Reviewed AI crawler labels in the AI activity view.
Allowed the wanted crawler category, blocked the unwanted one, and fed the labels into the existing confidence signal.
Open the web ACL and confirm the AI handling rules:
https://us-east-1.console.aws.amazon.com/wafv2/homev2/web-acls?region=us-east-1
```
### Troubleshooting
#### Cannot see AI crawlers
Enable Bot Control and use the AI activity view (Step 1).
#### Handling is all-or-nothing
Add per-category label-match rules (Step 2).
## Security Considerations
This procedure modifies a security control, so misconfiguration directly weakens the application's defenses.
- **Least-privilege IAM.** You MUST grant only the specific `wafv2:` actions a task needs (for example `wafv2:GetWebACL` and `wafv2:UpdateWebACL`) rather than `wafv2:*` or the `AWSWAFFullAccess` managed policy.
- **Ephemeral credentials.** You MUST use IAM roles with temporary credentials (such as an EC2 instance profile, SSO session, or `aws sts assume-role`) rather than long-lived IAM user access keys when running these WAF CLI commands.
- **Monitor configuration changes.** You SHOULD enable AWS CloudTrail on `wafv2` management events and set CloudWatch alarms on critical web ACL configuration changes (such as `DeleteWebACL` and `UpdateWebACL` rule removals) and on the web ACL's `BlockedRequests` and `CountedRequests` metrics, so rule changes and sudden spikes in blocked or counted traffic are detected.
## Additional Resources
- [AWS WAF Bot Control rule group (AWS WAF Developer Guide)](https://docs.aws.amazon.com/waf/latest/developerguide/aws-managed-rule-groups-bot.html)
- [AWS WAF label match rule statement (AWS WAF Developer Guide)](https://docs.aws.amazon.com/waf/latest/developerguide/waf-rule-label-match-statement.html)
references/setting-up-logging-and-request-sampling.md
# Setting Up Logging and Request Sampling
## Overview
Domain expertise for getting AWS WAF logging and request sampling working before any rule is
enabled, so Count-mode tuning has data to read. Covers the destination choice (Amazon CloudWatch
Logs, Amazon S3, or Amazon Data Firehose) and its traps (the `aws-waf-logs-` naming prefix, the
CloudFront-logs-in-us-east-1 rule), redacting sensitive fields, and confirming logs flow before
rules go on.
Does not cover the rules themselves; those are separate references. This reference is the
prerequisite the tuning workflows assume.
Execute commands using the AWS MCP server when connected (sandboxed execution, audit logging,
observability). Fall back to the AWS CLI otherwise.
## Table of Contents
- Overview
- Workflow
- Decision: logging destination
- Naming and Region constraints
- Redact sensitive fields
- Confirm logs are flowing before enabling rules
- Troubleshooting
- Procedure
- Security Considerations
- Additional Resources
## Workflow
To set up logging and sampling end to end, follow the procedure exactly. See the Procedure section
below.
The procedure covers:
- Choosing a logging destination and meeting its naming and Region constraints
- Redacting sensitive fields before logging is enabled
- Enabling logging on the web ACL and confirming logs flow
- Confirming request sampling is on
## Decision: logging destination
| Destination | Best for | Latency |
| --- | --- | --- |
| CloudWatch Logs | Real-time analysis with Logs Insights and dashboards | Seconds |
| Amazon S3 | Long-term retention and Athena queries | Minutes |
| Amazon Data Firehose | Streaming to a SIEM or OpenSearch | Seconds |
**Constraints:**
- You SHOULD match the destination to the customer's need: CloudWatch Logs for real-time review, S3
for retention and query, Firehose for streaming to a SIEM
## Naming and Region constraints
The destination has naming and Region traps that cause logs to silently never arrive.
**Constraints:**
- You MUST give the log destination a name carrying the `aws-waf-logs-` prefix; without it, logs
fail silently
- You MUST send a CloudFront web ACL's logs to a destination in `us-east-1`
- You SHOULD confirm the destination's resource policy allows AWS WAF log delivery before enabling
- You MUST include `aws:SourceArn` and `aws:SourceAccount` condition keys in the log destination's
resource policy to restrict delivery to the specific web ACL and account and prevent
confused-deputy attacks
- You MUST enable encryption at rest on the log destination (CloudWatch Logs, Amazon S3, or Amazon
Data Firehose, ideally with a customer-managed KMS key), since the logs can capture sensitive
fields
- You MUST ensure the log destination accepts delivery only over encrypted channels: CloudWatch Logs
delivery uses HTTPS, the S3 bucket policy MUST enforce `aws:SecureTransport`, and Firehose MUST use
HTTPS
## Redact sensitive fields
Logging full requests can capture credentials and session cookies in plain text.
**Constraints:**
- You MUST redact sensitive fields such as the `authorization` header and `cookie` before enabling
logging, so secrets are not written to the destination
- You SHOULD confirm with the customer which fields carry sensitive data for their application
## Confirm logs are flowing before enabling rules
Customers assume logging works, enable rules, and find the destination was misconfigured and
captured nothing during the tuning window.
**Constraints:**
- You MUST confirm logs are arriving at the destination before any rule is enabled
- You SHOULD confirm request sampling is on (it is part of the web ACL visibility config) so
sampled requests are available for tuning
## Troubleshooting
### No logs arrive at the destination
The destination name is missing the `aws-waf-logs-` prefix, or its resource policy does not allow
AWS WAF delivery. Fix the name or policy (Naming and Region constraints).
### A CloudFront web ACL produces no logs
The destination is not in `us-east-1`. Create a destination there (Naming and Region constraints).
### Sensitive fields appear in logs
No redaction is configured. Add redacted fields for `authorization` and `cookie` (Redact sensitive
fields).
## Procedure
### Overview
This procedure chooses a logging destination, applies redaction, enables logging on the web ACL,
and confirms logs flow, then surfaces the console link.
### Parameters
- **web_acl_arn** (required): The ARN of the web ACL to log.
- **scope** (required): `CLOUDFRONT` or `REGIONAL`.
- **destination_arn** (required): The ARN of the log destination, its name carrying the
`aws-waf-logs-` prefix.
- **redacted_fields** (required): The fields to redact (for example `authorization`, `cookie`).
**Constraints for parameter acquisition:**
- You MUST ask for all required parameters upfront in a single prompt
- You MUST confirm a CloudFront web ACL's destination is in `us-east-1`
### Steps
#### 1. Verify dependencies
**Constraints:**
- You MUST confirm credentials with `aws sts get-caller-identity`
- You MUST confirm the destination name carries the `aws-waf-logs-` prefix
- You MUST verify or enable encryption at rest on the log destination before enabling logging,
since WAF logs can capture credentials and session data. Use the mechanism for the destination
type:
```
# CloudWatch Logs: attach a KMS key to the log group
aws logs associate-kms-key --log-group-name {log_group_name} --kms-key-id {kms_key_arn} --region {region}
# Amazon S3: confirm default SSE (SSE-S3 or SSE-KMS) is set on the bucket
aws s3api get-bucket-encryption --bucket {bucket_name}
# Amazon Data Firehose: confirm server-side encryption is enabled on the stream
aws firehose describe-delivery-stream --delivery-stream-name {stream_name} --region {region}
```
#### 2. Enable logging with redaction
**Constraints:**
- You MUST put the logging configuration with the redacted fields, passing the whole
`--logging-configuration` as one JSON string (mixing CLI shorthand with inline JSON fails to
parse, and `LogDestinationConfigs` is a list):
```
aws wafv2 put-logging-configuration \
--logging-configuration '{"ResourceArn":"{web_acl_arn}","LogDestinationConfigs":["{destination_arn}"],"RedactedFields":[{"SingleHeader":{"Name":"authorization"}},{"SingleHeader":{"Name":"cookie"}}]}' \
--region {region}
```
#### 3. Confirm logs are flowing
**Constraints:**
- You MUST confirm log records are arriving at the destination before any rule is enabled
- You MUST confirm `SampledRequestsEnabled` is true on the web ACL visibility config
#### 4. Surface the console link
**Constraints:**
- You MUST present the web ACL console link and tell the customer to open the Logging and metrics
tab to confirm logging is enabled:
```
https://us-east-1.console.aws.amazon.com/wafv2/homev2/web-acls?region={region}
```
### Example
#### Example input
```json
{
"web_acl_arn": "arn:aws:wafv2:us-east-1:111122223333:regional/webacl/example-webacl/abc",
"scope": "REGIONAL",
"destination_arn": "arn:aws:logs:us-east-1:111122223333:log-group:aws-waf-logs-example",
"redacted_fields": ["authorization", "cookie"]
}
```
#### Example output
```
Enabled logging for web ACL example-webacl to aws-waf-logs-example, redacting authorization and cookie.
Confirmed log records are arriving and request sampling is on.
Logging is ready — Count-mode tuning now has data to read.
Open the web ACL Logging and metrics tab to confirm:
https://us-east-1.console.aws.amazon.com/wafv2/homev2/web-acls?region=us-east-1
```
### Troubleshooting
#### No logs arrive
The destination name lacks the `aws-waf-logs-` prefix or its policy blocks delivery (Step 1).
#### A CloudFront web ACL produces nothing
The destination is not in `us-east-1` (Step 1).
## Security Considerations
This procedure modifies a security control, so misconfiguration directly weakens the application's defenses.
- **Least-privilege IAM.** You MUST grant only the specific `wafv2:` actions a task needs (for example `wafv2:GetWebACL` and `wafv2:UpdateWebACL`) rather than `wafv2:*` or the `AWSWAFFullAccess` managed policy.
- **Ephemeral credentials.** You MUST use IAM roles with temporary credentials (such as an EC2 instance profile, SSO session, or `aws sts assume-role`) rather than long-lived IAM user access keys when running these WAF CLI commands.
- **Monitor configuration changes.** You SHOULD enable AWS CloudTrail on `wafv2` management events and set CloudWatch alarms on critical web ACL configuration changes (such as `DeleteWebACL` and `UpdateWebACL` rule removals) and on the web ACL's `BlockedRequests` and `CountedRequests` metrics, so rule changes and sudden spikes in blocked or counted traffic are detected.
- **Protect log destinations.** Logs can capture credentials and session data. You MUST redact sensitive fields (such as the `authorization` header and `cookie`) and MUST enable encryption at rest on the log destination (CloudWatch Logs, Amazon S3, or Amazon Data Firehose).
## Additional Resources
- [Logging AWS WAF web ACL traffic (AWS WAF Developer Guide)](https://docs.aws.amazon.com/waf/latest/developerguide/logging.html)
- [Testing and tuning your AWS WAF protections (AWS WAF Developer Guide)](https://docs.aws.amazon.com/waf/latest/developerguide/web-acl-testing.html)
references/stripping-inbound-waf-headers-before-trusting-them.md
# Stripping Inbound x-amzn-waf-* Headers Before Trusting Them
## Overview
Domain expertise for the mandatory safety companion to any AWS WAF header forwarding: a block rule
that rejects inbound requests already carrying an `x-amzn-waf-*` header, so an attacker cannot
forge the signal the origin trusts. Covers why the gap exists (AWS WAF does not strip pre-existing
`x-amzn-waf-*` headers before inserting its own) and the rule placement.
Does not cover the forwarding workflows themselves; this is their required companion. The
confidence-signal, interpolation, and client-IP references all point here.
Execute commands using the AWS MCP server when connected (sandboxed execution, audit logging,
observability). Fall back to the AWS CLI otherwise.
## Table of Contents
- Overview
- Workflow
- Why the spoofing gap exists
- A mandatory companion, not optional hardening
- Troubleshooting
- Procedure
- Security Considerations
- Additional Resources
## Workflow
To add the inbound-header-stripping rule end to end, follow the procedure exactly. See the
Procedure section below.
The procedure covers:
- Adding a block rule matching any inbound `x-amzn-waf-*` header
- Placing it before the forwarding rules
- Confirming and surfacing the console link
## Why the spoofing gap exists
AWS WAF custom request handling does not strip a pre-existing `x-amzn-waf-*` header before inserting
its own. A request that arrives already carrying one passes that forged value to the origin, so an
origin trusting `x-amzn-waf-bot-category:verified`, a spoofed confidence value, or a spoofed client
IP is bypassable.
**Constraints:**
- You MUST add a block rule that rejects any inbound request already carrying an `x-amzn-waf-*`
header
- You MUST place that rule at a lower priority number than the forwarding rules, so it runs first
and only AWS-WAF-set values reach the origin
## A mandatory companion, not optional hardening
The vulnerability is the absence of a rule, not a misconfiguration that throws an error, so it is
easy to ship header forwarding without it.
**Constraints:**
- You MUST treat this rule as a mandatory companion whenever the skill recommends header forwarding,
not an optional hardening step
- You SHOULD confirm the stripping rule is present before declaring any forwarding workflow complete
## Troubleshooting
### The origin trusts a forged signal
No stripping rule is present, or it runs after the forwarding rules. Add it before them (Why the
spoofing gap exists).
### Legitimate requests are blocked by the strip rule
A legitimate upstream is setting an `x-amzn-waf-*` header. That is unusual; confirm the upstream and
narrow the match if a specific known header must pass, but default to blocking all inbound
`x-amzn-waf-*`.
## Procedure
### Overview
This procedure adds a block rule for inbound `x-amzn-waf-*` headers before the forwarding rules,
then surfaces the console link.
### Parameters
- **web_acl_name**, **web_acl_id**, **scope** (required): Identify the web ACL.
**Constraints for parameter acquisition:**
- You MUST ask for all required parameters upfront in a single prompt
- You MUST confirm which forwarding rules exist so the strip rule is placed before them
### Steps
#### 1. Verify dependencies
**Constraints:**
- You MUST confirm credentials with `aws sts get-caller-identity`
- You MUST identify the priority numbers of the forwarding rules
#### 2. Add the inbound strip rule
**Constraints:**
- You MUST add a block rule matching any request that carries an `x-amzn-waf-*` header (a header
match on the `x-amzn-waf-` prefix), at a lower priority number than the forwarding rules
- You MUST fetch the current `LockToken` before `update-web-acl` and pass the full rule set
- You MUST preserve the web ACL's existing `DefaultAction` from the `get-web-acl` response and pass
it back as `{default_action}`; do not assume `Allow={}`, since that would silently open all
unmatched traffic on a web ACL whose default action is `Block`. For example, a Block rule whose
`ByteMatchStatement` matches the `x-amzn-waf-` prefix on header keys:
```
aws wafv2 get-web-acl --name {web_acl_name} --scope {scope} --id {web_acl_id} --region {region}
aws wafv2 update-web-acl --name {web_acl_name} --scope {scope} --id {web_acl_id} \
--lock-token {lock_token} --default-action {default_action} \
--rules '[{"Name":"StripInboundWafHeaders","Priority":0,"Action":{"Block":{}},"Statement":{"ByteMatchStatement":{"SearchString":"x-amzn-waf-","PositionalConstraint":"STARTS_WITH","FieldToMatch":{"Headers":{"MatchPattern":{"All":{}},"MatchScope":"KEY","OversizeHandling":"MATCH"}},"TextTransformations":[{"Priority":0,"Type":"LOWERCASE"}]}},"VisibilityConfig":{"SampledRequestsEnabled":true,"CloudWatchMetricsEnabled":true,"MetricName":"StripInboundWafHeaders"}}]' \
--visibility-config SampledRequestsEnabled=true,CloudWatchMetricsEnabled=true,MetricName={web_acl_name} \
--region {region}
```
#### 3. Confirm and surface the console link
**Constraints:**
- You MUST confirm the strip rule runs before the forwarding rules
- You MUST present the web ACL console link and tell the customer to confirm the rule order:
```
https://us-east-1.console.aws.amazon.com/wafv2/homev2/web-acls?region={region}
```
### Example
#### Example input
```json
{
"web_acl_name": "example-webacl",
"web_acl_id": "abc",
"scope": "REGIONAL"
}
```
#### Example output
```
Added a Block rule rejecting any inbound request carrying an x-amzn-waf-* header, at priority 0 (before the forwarding rules).
Open the web ACL and confirm the strip rule runs first:
https://us-east-1.console.aws.amazon.com/wafv2/homev2/web-acls?region=us-east-1
```
### Troubleshooting
#### The origin trusts a forged value
The strip rule is missing or runs after the forwarding rules. Place it before them (Step 2).
## Security Considerations
This procedure modifies a security control, so misconfiguration directly weakens the application's defenses.
- **Least-privilege IAM.** You MUST grant only the specific `wafv2:` actions a task needs (for example `wafv2:GetWebACL` and `wafv2:UpdateWebACL`) rather than `wafv2:*` or the `AWSWAFFullAccess` managed policy.
- **Ephemeral credentials.** You MUST use IAM roles with temporary credentials (such as an EC2 instance profile, SSO session, or `aws sts assume-role`) rather than long-lived IAM user access keys when running these WAF CLI commands.
- **Monitor configuration changes.** You SHOULD enable AWS CloudTrail on `wafv2` management events and set CloudWatch alarms on critical web ACL configuration changes (such as `DeleteWebACL` and `UpdateWebACL` rule removals) and on the web ACL's `BlockedRequests` and `CountedRequests` metrics, so rule changes and sudden spikes in blocked or counted traffic are detected.
- **Header-spoofing risk.** Any `x-amzn-waf-*` signal forwarded to the origin can be forged inbound. You MUST add the inbound-header-stripping rule whenever a signal or client IP is forwarded (see stripping-inbound-waf-headers-before-trusting-them); without it the origin trusts a spoofable value.
## Additional Resources
- [Customizing web requests and responses in AWS WAF (AWS WAF Developer Guide)](https://docs.aws.amazon.com/waf/latest/developerguide/waf-custom-request-response.html)
- [How to use AWS WAF Bot Control for targeted bots signals and mitigate evasive bots with adaptive user experience (AWS Networking & Content Delivery Blog)](https://aws.amazon.com/blogs/networking-and-content-delivery/how-to-use-aws-waf-bot-control-for-targeted-bots-signals-and-mitigate-evasive-bots-with-adaptive-user-experience/)
references/turning-bot-control-labels-into-a-confidence-signal.md
# Turning Bot Control Labels into a Confidence Signal
## Overview
Domain expertise for collapsing the many Bot Control Targeted labels into a single
application-facing confidence signal. Covers overriding the Targeted rules to the non-terminating
Challenge action, mapping many `TGT_*` labels into one `x-amzn-waf-bot-confidence` header with
label-match rules, defining the mapping in the web ACL so the application contract stays fixed, and
the mandatory inbound-header-stripping companion.
Does not cover turning Bot Control on (that is the protecting-against-bots reference), forwarding a
whole namespace with interpolation (its own reference), or the application's response (the adaptive
mitigation reference).
Execute commands using the AWS MCP server when connected (sandboxed execution, audit logging,
observability). Fall back to the AWS CLI otherwise.
## Table of Contents
- Overview
- Workflow
- Override Targeted rules to Challenge
- Collapse labels into one confidence header
- Define the mapping in the web ACL
- Strip inbound headers
- Troubleshooting
- Procedure
- Security Considerations
- Additional Resources
## Workflow
To turn Bot Control labels into a confidence signal end to end, follow the procedure exactly. See
the Procedure section below.
The procedure covers:
- Overriding the Targeted rules to Challenge so evaluation continues
- Mapping label groups to low, medium, and high with OR label-match rules
- Forwarding one `x-amzn-waf-bot-confidence` header to the origin
- Adding the inbound-header-stripping rule
## Override Targeted rules to Challenge
Block or CAPTCHA terminate evaluation, so later rules that forward the signal never run. Challenge
is non-terminating with a valid token.
**Constraints:**
- You MUST override the Targeted rules to Challenge when the goal is to forward a signal rather than
block at the edge, so AWS WAF keeps evaluating later rules
- You MUST NOT use a terminating action (Block, CAPTCHA) on the rules whose labels feed the signal
## Collapse labels into one confidence header
Targeted produces hundreds of `TGT_*` labels. The application cannot act on all of them, so map
groups of labels to a small set of confidence levels.
**Constraints:**
- You MUST collapse the labels into a single confidence signal (low, medium, high) using OR
label-match rules, rather than exposing raw labels to the application
- You MUST place the label-match rules at a higher priority number than the Bot Control group, so
the labels exist when the matches run
## Define the mapping in the web ACL
If the application keys on specific label names, it has to change whenever AWS adds or renames a
label. Defining the mapping in the web ACL keeps the application contract fixed.
**Constraints:**
- You MUST define the label-to-confidence mapping inside the web ACL and forward one stable header
(`x-amzn-waf-bot-confidence`), so the application contract does not change as labels evolve
## Strip inbound headers
The confidence header is an `x-amzn-waf-*` header, which an attacker can set inbound unless it is
stripped first.
**Constraints:**
- You MUST pair this workflow with the inbound-header-stripping rule (see
stripping-inbound-waf-headers-before-trusting-them), placed before the forwarding rules, so the
signal cannot be forged
- You MUST forward `x-amzn-waf-bot-confidence` only to an HTTPS-only origin and SHOULD have the
application set HSTS, so the confidence signal is not exposed in cleartext. You SHOULD use
AWS Certificate Manager (ACM) to provision and manage the origin's TLS certificate (for example on
an Application Load Balancer), so the certificate is validated and automatically renewed
## Troubleshooting
### The forwarding rule never runs
A terminating action on an earlier rule stopped evaluation. Override the Targeted rules to Challenge
(Override Targeted rules to Challenge).
### The label-match rules do not see the labels
They are at a lower priority number than the Bot Control group. Move them after it (Collapse labels
into one confidence header).
### The application receives a spoofed confidence value
No inbound stripping rule is in place. Add it before the forwarding rules (Strip inbound headers).
## Procedure
### Overview
This procedure overrides the Targeted rules to Challenge, maps labels to a confidence header,
strips spoofed inbound headers, and surfaces the console link.
### Parameters
- **web_acl_name**, **web_acl_id**, **scope** (required): Identify the web ACL.
- **confidence_mapping** (required): Which label groups map to low, medium, and high.
**Constraints for parameter acquisition:**
- You MUST ask for all required parameters upfront in a single prompt
- You MUST confirm Bot Control Targeted is already enabled (see the protecting-against-bots
reference)
### Steps
#### 1. Verify dependencies
**Constraints:**
- You MUST confirm credentials with `aws sts get-caller-identity`
- You MUST confirm Bot Control Targeted is enabled and producing labels
#### 2. Override Targeted rules to Challenge
**Constraints:**
- You MUST override the Targeted rules to Challenge so evaluation continues to the forwarding rules
- You MUST fetch the current `LockToken` before `update-web-acl` and pass the full rule set
- You MUST preserve the web ACL's existing `DefaultAction` from the `get-web-acl` response and pass
it back as `{default_action}`; do not assume `Allow={}`, since that would silently open all
unmatched traffic on a web ACL whose default action is `Block`. For example, the Bot Control group
with `RuleActionOverrides` setting Targeted rules to Challenge:
```
aws wafv2 get-web-acl --name {web_acl_name} --scope {scope} --id {web_acl_id} --region {region}
aws wafv2 update-web-acl --name {web_acl_name} --scope {scope} --id {web_acl_id} \
--lock-token {lock_token} --default-action {default_action} \
--rules '[{"Name":"AWS-BotControl","Priority":1,"Statement":{"ManagedRuleGroupStatement":{"VendorName":"AWS","Name":"AWSManagedRulesBotControlRuleSet","ManagedRuleGroupConfigs":[{"AWSManagedRulesBotControlRuleSet":{"InspectionLevel":"TARGETED"}}],"RuleActionOverrides":[{"Name":"TGT_VolumetricSession","ActionToUse":{"Challenge":{}}}]}},"OverrideAction":{"None":{}},"VisibilityConfig":{"SampledRequestsEnabled":true,"CloudWatchMetricsEnabled":true,"MetricName":"AWS-BotControl"}}]' \
--visibility-config SampledRequestsEnabled=true,CloudWatchMetricsEnabled=true,MetricName={web_acl_name} \
--region {region}
```
#### 3. Map labels to a confidence header
**Constraints:**
- You MUST add OR label-match rules that map label groups to low, medium, and high, at a higher
priority number than the Bot Control group
- You MUST forward one `x-amzn-waf-bot-confidence` header via custom request handling. For example,
a Count rule that matches a label group and inserts the header (insert this rule with a fresh
`LockToken` alongside the rest of the rule set):
```
--rules '[{"Name":"ConfidenceHigh","Priority":10,"Action":{"Count":{"CustomRequestHandling":{"InsertHeaders":[{"Name":"x-amzn-waf-bot-confidence","Value":"high"}]}}},"Statement":{"LabelMatchStatement":{"Scope":"LABEL","Key":"awswaf:managed:aws:bot-control:targeted:aggregate:volumetric:session:maximum"}},"VisibilityConfig":{"SampledRequestsEnabled":true,"CloudWatchMetricsEnabled":true,"MetricName":"ConfidenceHigh"}}]'
```
#### 4. Strip inbound headers and surface the console link
**Constraints:**
- You MUST add the inbound `x-amzn-waf-*` stripping rule before the forwarding rules
- You MUST present the web ACL console link and tell the customer to confirm the rule order:
```
https://us-east-1.console.aws.amazon.com/wafv2/homev2/web-acls?region={region}
```
### Example
#### Example input
```json
{
"web_acl_name": "example-webacl",
"web_acl_id": "abc",
"scope": "REGIONAL",
"confidence_mapping": {"low": ["TGT_VolumetricSession"], "high": ["TGT_VolumetricSessionMaximum"]}
}
```
#### Example output
```
Overrode Targeted rules to Challenge so evaluation continues.
Mapped label groups to x-amzn-waf-bot-confidence (low/medium/high) and added an inbound strip rule first.
Open the web ACL and confirm the rule order:
https://us-east-1.console.aws.amazon.com/wafv2/homev2/web-acls?region=us-east-1
```
### Troubleshooting
#### The forwarding rule never runs
An earlier terminating action stopped evaluation. Use Challenge (Step 2).
#### Labels are not visible to the match rules
Priority order is wrong. Move the label-match rules after the Bot Control group (Step 3).
## Security Considerations
This procedure modifies a security control, so misconfiguration directly weakens the application's defenses.
- **Least-privilege IAM.** You MUST grant only the specific `wafv2:` actions a task needs (for example `wafv2:GetWebACL` and `wafv2:UpdateWebACL`) rather than `wafv2:*` or the `AWSWAFFullAccess` managed policy.
- **Ephemeral credentials.** You MUST use IAM roles with temporary credentials (such as an EC2 instance profile, SSO session, or `aws sts assume-role`) rather than long-lived IAM user access keys when running these WAF CLI commands.
- **Monitor configuration changes.** You SHOULD enable AWS CloudTrail on `wafv2` management events and set CloudWatch alarms on critical web ACL configuration changes (such as `DeleteWebACL` and `UpdateWebACL` rule removals) and on the web ACL's `BlockedRequests` and `CountedRequests` metrics, so rule changes and sudden spikes in blocked or counted traffic are detected.
- **Header-spoofing risk.** Any `x-amzn-waf-*` signal forwarded to the origin can be forged inbound. You MUST add the inbound-header-stripping rule whenever a signal or client IP is forwarded (see stripping-inbound-waf-headers-before-trusting-them); without it the origin trusts a spoofable value.
- **Encrypted transport.** You MUST forward the signal only to an HTTPS-only origin and the application MUST validate the origin's TLS certificate, so the signal is not exposed in cleartext. You SHOULD use AWS Certificate Manager (ACM) to provision and manage the origin's TLS certificate (for example on an Application Load Balancer), so the certificate is validated and automatically renewed.
- **Defense in depth.** You SHOULD treat the forwarded signal as one input among several, not a sole gate; over-relying on a single signal without defense in depth leaves the application exposed if the signal is evaded or degraded.
## Additional Resources
- [AWS WAF label match rule statement (AWS WAF Developer Guide)](https://docs.aws.amazon.com/waf/latest/developerguide/waf-rule-label-match-statement.html)
- [AWS WAF rule action (AWS WAF Developer Guide)](https://docs.aws.amazon.com/waf/latest/developerguide/waf-rule-action.html)
- [Customizing web requests and responses in AWS WAF (AWS WAF Developer Guide)](https://docs.aws.amazon.com/waf/latest/developerguide/waf-custom-request-response.html)
- [How to use AWS WAF Bot Control for targeted bots signals and mitigate evasive bots with adaptive user experience (AWS Networking & Content Delivery Blog)](https://aws.amazon.com/blogs/networking-and-content-delivery/how-to-use-aws-waf-bot-control-for-targeted-bots-signals-and-mitigate-evasive-bots-with-adaptive-user-experience/)
references/using-ip-sets-and-geographic-match-rules.md
# Using IP Sets and Geographic Match Rules
## Overview
Domain expertise for allow and block lists in AWS WAF based on source IP range or country. Covers
the IP set scope that must match the web ACL, the fact that a geographic match matches only at the
country level (region-level needs a paired label-match rule), the forwarded-IP configuration needed
behind a proxy, and keeping a compliance country list current without hand edits.
Does not cover rate-based rules, managed rules, bot, or fraud rule groups; those are separate
references. Recovering the real client IP behind a CDN has its own reference that this one points
at.
Execute commands using the AWS MCP server when connected (sandboxed execution, audit logging,
observability). Fall back to the AWS CLI otherwise.
## Table of Contents
- Overview
- Workflow
- IP set scope must match the web ACL
- Geographic match is country-level by default
- Reading the real client IP behind a proxy
- Keeping a compliance country list current
- Troubleshooting
- Procedure
- Security Considerations
- Additional Resources
## Workflow
To add IP set or geographic match rules end to end, follow the procedure exactly. See the Procedure
section below.
The procedure covers:
- Creating an IP set in the scope that matches the web ACL
- Adding an IP set match or geographic match statement with an allow or block action
- Configuring forwarded-IP reading when behind a proxy
- Confirming and surfacing the console link
## IP set scope must match the web ACL
An IP set used with a CloudFront web ACL must be in the Global (CloudFront) scope; a regional web
ACL needs a regional IP set in the same Region. A scope mismatch means the web ACL cannot reference
the IP set.
**Constraints:**
- You MUST create the IP set in the scope that matches the protected resource type before
referencing it
- You MUST NOT expect a regional IP set to be usable from a CloudFront web ACL or vice versa
## Geographic match is country-level by default
A geographic match statement matches by itself only at the country level. Region-level (sub-country)
matching requires a geo match rule followed by a label-match rule. Customers try a single rule for
a region and it silently does not match.
**Constraints:**
- You MUST pair a geo match rule with a label-match rule when the customer asks for region-level
control, rather than expecting the single statement to match a sub-country region
- You SHOULD note that the geo match adds a country label automatically that the label-match rule
then keys on
## Reading the real client IP behind a proxy
By default a geo match reads the country from the request origin IP, which is the proxy or load
balancer rather than the real client, so the wrong sources are matched.
**Constraints:**
- You MUST enable forwarded-IP configuration to read the client address from a header such as
`X-Forwarded-For` when the application sits behind a proxy
- You SHOULD trust the forwarded header only from a known upstream, and pair it with the
inbound-header-stripping reference (see stripping-inbound-waf-headers-before-trusting-them)
## Keeping a compliance country list current
Customers maintaining a blocked-country list for compliance edit it by hand and it drifts out of
date.
**Constraints:**
- You SHOULD point at the automated pattern that keeps the blocked-country list current from a
configuration source, rather than relying on manual edits, when the list backs a compliance
requirement
## Troubleshooting
### The web ACL cannot reference the IP set
The IP set is in the wrong scope. Recreate it in the scope that matches the web ACL (IP set scope
must match the web ACL).
### A region-level geo rule does not match
A single geo match statement is country-level only. Pair it with a label-match rule (Geographic
match is country-level by default).
### Geo matching blocks the wrong sources
The rule reads the proxy IP. Enable forwarded-IP configuration (Reading the real client IP behind a
proxy).
## Procedure
### Overview
This procedure creates an IP set in the right scope, adds an IP set or geographic match rule, and
surfaces the console link.
### Parameters
- **web_acl_name**, **web_acl_id**, **scope** (required): Identify the web ACL.
- **match_type** (required): `ip_set` or `geo`.
- **addresses** (required for `ip_set`): The IP addresses and CIDR ranges.
- **country_codes** (required for `geo`): The country codes to match.
- **action** (required): `Allow` or `Block`.
- **forwarded_ip** (optional): Whether to read the client IP from a forwarding header.
**Constraints for parameter acquisition:**
- You MUST ask for all required parameters upfront in a single prompt
- You MUST set the IP set scope to match the web ACL before creating it
### Steps
#### 1. Verify dependencies
**Constraints:**
- You MUST confirm credentials with `aws sts get-caller-identity`
#### 2. Create the IP set (for an IP set rule)
**Constraints:**
- You MUST create the IP set in the matching scope:
```
aws wafv2 create-ip-set --name {name} --scope {scope} --ip-address-version IPV4 \
--addresses {addresses} --region {region}
```
- You MUST treat `update-ip-set` as a full replacement; pass the complete merged list, not just new
entries
#### 3. Add the match rule
**Constraints:**
- You MUST add the IP set match or geographic match statement with the chosen action, fetching the
current `LockToken` immediately before `update-web-acl` and passing the full rule set
- You MUST preserve the web ACL's existing `DefaultAction` from the `get-web-acl` response and pass
it back as `{default_action}`; do not assume `Allow={}`, since that would silently open all
unmatched traffic on a web ACL whose default action is `Block`
- You MUST add a paired label-match rule for region-level geo control
- You MUST enable forwarded-IP configuration when the application is behind a proxy
#### 4. Confirm and surface the console link
**Constraints:**
- You MUST present the web ACL console link and tell the customer to open it and confirm the rule:
```
https://us-east-1.console.aws.amazon.com/wafv2/homev2/web-acls?region={region}
```
### Example
#### Example input
```json
{
"web_acl_name": "example-webacl",
"web_acl_id": "abc",
"scope": "REGIONAL",
"match_type": "geo",
"country_codes": ["KP", "IR"],
"action": "Block",
"forwarded_ip": true
}
```
#### Example output
```
Added a geo-match Block rule for KP, IR, reading the client IP from X-Forwarded-For.
Open the web ACL and confirm the rule:
https://us-east-1.console.aws.amazon.com/wafv2/homev2/web-acls?region=us-east-1
```
### Troubleshooting
#### The IP set cannot be referenced
Scope mismatch. Recreate the IP set in the web ACL's scope (Step 2).
#### A sub-country region does not match
Geo match is country-level. Add a label-match rule (Step 3).
## Security Considerations
This procedure modifies a security control, so misconfiguration directly weakens the application's defenses.
- **Least-privilege IAM.** You MUST grant only the specific `wafv2:` actions a task needs (for example `wafv2:GetWebACL` and `wafv2:UpdateWebACL`) rather than `wafv2:*` or the `AWSWAFFullAccess` managed policy.
- **Ephemeral credentials.** You MUST use IAM roles with temporary credentials (such as an EC2 instance profile, SSO session, or `aws sts assume-role`) rather than long-lived IAM user access keys when running these WAF CLI commands.
- **Monitor configuration changes.** You SHOULD enable AWS CloudTrail on `wafv2` management events and set CloudWatch alarms on critical web ACL configuration changes (such as `DeleteWebACL` and `UpdateWebACL` rule removals) and on the web ACL's `BlockedRequests` and `CountedRequests` metrics, so rule changes and sudden spikes in blocked or counted traffic are detected.
- **Header-spoofing risk.** Any `x-amzn-waf-*` signal forwarded to the origin can be forged inbound. You MUST add the inbound-header-stripping rule whenever a signal or client IP is forwarded (see stripping-inbound-waf-headers-before-trusting-them); without it the origin trusts a spoofable value.
## Additional Resources
- [Creating and managing an IP set in AWS WAF (AWS WAF Developer Guide)](https://docs.aws.amazon.com/waf/latest/developerguide/waf-ip-set-managing.html)
- [Geographic match rule statement (AWS WAF Developer Guide)](https://docs.aws.amazon.com/waf/latest/developerguide/waf-rule-statement-type-geo-match.html)
- [How to use AWS WAF to filter incoming traffic from embargoed countries (AWS Security Blog)](https://aws.amazon.com/blogs/security/how-to-use-aws-waf-to-filter-incoming-traffic-from-embargoed-countries/)
SKILL.md
---
name: waf
description: >-
Configures AWS WAF to filter web traffic: creating web access control lists (web ACLs) on
CloudFront, Application Load Balancers, API Gateway, and AppSync; AWS Managed Rules tuned in Count
mode; rate-based rules for HTTP floods; IP set and geographic match rules; Bot Control (Common and
Targeted); turning bot labels into a confidence signal; stripping spoofed inbound x-amzn-waf-*
headers; recovering the real client IP behind a CDN; Fraud Control (account takeover and account
creation fraud prevention); and logging and request sampling. Use when the user wants to protect a
web application or API from common exploits, bots, credential stuffing, fake-account creation, or
HTTP floods at the application layer (layer 7). Routes to the right per-task procedure in
references. Do NOT use for L3/L4 DDoS protection (shieldadvanced skill), multi-account WAF rollout
(firewallmanager skill), CloudFront configuration (cloudfront skill), or Route 53 health checks or
records (route53 skill).
version: 1
---
# AWS WAF
## Overview
Domain expertise for configuring AWS WAF, the web application firewall that filters HTTP and HTTPS
traffic to CloudFront distributions, Application Load Balancers, API Gateway REST APIs, and AppSync
GraphQL APIs. Covers web ACL creation and association, AWS Managed Rules, rate-based rules, match
rules (IP set and geographic), Bot Control and the signal-forwarding workflows built on top of it,
Fraud Control for logins and signups, AI and LLM crawler management, and the logging that every
tuning workflow depends on.
This skill is a router. Each customer task maps to a procedure file under `references/`. Read the
matching reference in full before acting, then follow its constraints and steps. The reference
files are self-contained: each carries its own decision tables, constraints, procedure, and
troubleshooting.
Execute commands using the AWS MCP server when connected (sandboxed execution, audit logging,
observability). Fall back to the AWS CLI otherwise. A web ACL's scope is fixed at creation: a
CloudFront web ACL must be created in `us-east-1` with `CLOUDFRONT` scope, while a regional web ACL
(Application Load Balancer, API Gateway, AppSync) is created in the resource's Region with
`REGIONAL` scope.
## Which WAF task do you need?
| Goal | Reference |
| --- | --- |
| Create a web ACL and attach it to a resource | [creating a web ACL and associating it with a resource](references/creating-a-web-acl-and-associating-it-with-a-resource.md) |
| Set up logging and sampling before tuning anything | [setting up logging and request sampling](references/setting-up-logging-and-request-sampling.md) |
| Add AWS Managed Rules and tune false positives | [adding managed rules and tuning with count mode](references/adding-managed-rules-and-tuning-with-count-mode.md) |
| Throttle HTTP floods and brute force | [adding rate-based rules](references/adding-rate-based-rules.md) |
| Allow or block by IP range or country | [using ip sets and geographic match rules](references/using-ip-sets-and-geographic-match-rules.md) |
| Detect and control bots (the on-ramp) | [protecting against bots with bot control](references/protecting-against-bots-with-bot-control.md) |
| Collapse bot labels into one confidence signal | [turning bot control labels into a confidence signal](references/turning-bot-control-labels-into-a-confidence-signal.md) |
| Forward all signals to the origin with one rule | [forwarding signals with dynamic label interpolation](references/forwarding-signals-with-dynamic-label-interpolation.md) |
| Decide what the app does with the forwarded signal | [adaptive mitigation playbook for forwarded signals](references/adaptive-mitigation-playbook-for-forwarded-signals.md) |
| Stop attackers from spoofing forwarded headers | [stripping inbound waf headers before trusting them](references/stripping-inbound-waf-headers-before-trusting-them.md) |
| Recover the real client IP behind a CDN | [recovering the real client ip behind a cdn](references/recovering-the-real-client-ip-behind-a-cdn.md) |
| Protect logins and signups from fraud | [protecting logins and signups with fraud control](references/protecting-logins-and-signups-with-fraud-control.md) |
| See and manage AI and LLM crawler traffic | [seeing and managing ai crawler traffic](references/seeing-and-managing-ai-crawler-traffic.md) |
## Routing notes
- **Logging comes before tuning.** Every Count-mode tuning workflow assumes logging and request
sampling are on. If the customer has not set up logging, run that reference first; otherwise
Count-mode tuning has nothing to read.
- **Web ACL scope is fixed at creation.** A CloudFront web ACL is `CLOUDFRONT` scope in
`us-east-1`; a regional resource needs a `REGIONAL` web ACL in its own Region. Scope cannot be
changed later, so the creating reference settles it before anything is built.
- **Bot Control is a chain, not one task.** Protecting against bots is the on-ramp (turn on, choose
Common vs Targeted, observe). Turning labels into a confidence signal, forwarding that signal,
and deciding what the application does with it are three separate references that build on it in
that order. The header-stripping reference is the mandatory safety companion whenever a signal is
forwarded to the origin.
- **Common vs Targeted is not a soft choice.** Common only catches self-identifying bots and
known-bad IPs. For login, checkout, or any high-value endpoint facing evasive bots, Targeted with
the application integration SDK is required. The bots reference pushes Targeted for real bot
threats rather than presenting it as optional.
- **Rate limiting vs Fraud Control.** Rate-based rules blunt volumetric HTTP floods. Credential
stuffing and fake-account creation are account-based abuse that rate limiting misses; those go to
the Fraud Control reference (ATP and ACFP), not the rate-based reference.
- **Forwarded headers need the strip rule.** Any time the customer forwards a signal or the client
IP to the origin in `x-amzn-waf-*` headers, the inbound-header-stripping reference is required to
prevent spoofing. The confidence-signal, interpolation, and client-IP references all point at it.
- **What lives in other skills.** L3/L4 DDoS protection and Shield cost-protection credits are the
shieldadvanced skill. Multi-account WAF rollout is the firewallmanager skill. CloudFront and
Application Load Balancer configuration are their own skills. This skill builds the WAF rules; it
does not configure the resources it protects.
## Security Considerations
AWS WAF is itself a security control, so misconfiguration directly weakens an application's defenses.
Apply these across every reference:
- **Least-privilege IAM.** You MUST grant only the specific `wafv2:` actions a task needs (for
example `wafv2:CreateWebACL`, `wafv2:GetWebACL`, `wafv2:UpdateWebACL`, `wafv2:AssociateWebACL`,
`wafv2:PutLoggingConfiguration`) rather than `wafv2:*` or the `AWSWAFFullAccess` managed policy.
- **Ephemeral credentials.** You MUST use IAM roles with temporary credentials (such as an EC2
instance profile, SSO session, or `aws sts assume-role`) rather than long-lived IAM user access
keys when running these WAF CLI commands.
- **Monitor configuration changes.** You SHOULD enable AWS CloudTrail on `wafv2` management events
and set CloudWatch alarms on critical web ACL configuration changes (such as `DeleteWebACL` and
`UpdateWebACL` rule removals) and on the web ACL's `BlockedRequests` and `CountedRequests`
metrics, so rule changes and sudden spikes in blocked or counted traffic are detected.
- **Misconfiguration opens access.** A web ACL that is created but never associated, or one whose
default action is left at `Allow` with no enforcing rules, filters nothing. You MUST confirm the
web ACL is associated and that its posture matches the intended default (block vs allow) before
reporting setup complete.
- **Protect log destinations.** Logs can capture credentials and session data. You MUST redact
sensitive fields (such as the `authorization` header and `cookie`) and MUST enable encryption at
rest on the log destination (CloudWatch Logs, Amazon S3, or Amazon Data Firehose).
- **Header-spoofing risk.** Any `x-amzn-waf-*` signal forwarded to the origin can be forged inbound.
You MUST add the inbound-header-stripping rule whenever a signal or client IP is forwarded (see
stripping-inbound-waf-headers-before-trusting-them).
## Additional Resources
- [AWS WAF Developer Guide](https://docs.aws.amazon.com/waf/latest/developerguide/waf-chapter.html)
- [How AWS WAF works (AWS WAF Developer Guide)](https://docs.aws.amazon.com/waf/latest/developerguide/how-aws-waf-works.html)
- [AWS WAF pricing](https://aws.amazon.com/waf/pricing/)