Treat a WAF as a tuned filter, not as the application's authorization. When OWASP CRS blocks legitimate traffic, read the matched rule IDs and the target, then exclude only that rule or tag for that parameter on that route; never switch the engine off for the route. Start at paranoia level 1 with the default anomaly threshold of 5, keep the application fix that the exclusion now relies on, and handle bots by the business action they abuse.
- CRS adds a score for every matched rule and blocks when the inbound total reaches the threshold, 5 by default.
- Exclude by rule ID or tag, parameter and route:
ctl:ruleRemoveTargetByTag=attack-xss;ARGS:commentkept SQL injection checks on that field. - An exclusion moves the defense to the application: after it, a real
<script>payload in that field also passed, so output encoding must hold. - Paranoia levels 1 to 4 trade false positives for coverage; CRS 4 can detect at a higher level than it blocks while you tune.
- Bots abuse valid requests: map them to OWASP OATs such as OAT-008 credential stuffing and OAT-021 denial of inventory, and limit the business action.
| Setting | Value | Source |
|---|---|---|
tx.blocking_paranoia_level | 1, raised only with a tuning period | OWASP CRS anomaly scoring |
tx.detection_paranoia_level | Equal to the blocking level by default; set one level higher while tuning | OWASP CRS anomaly scoring |
| Inbound and outbound anomaly thresholds | 5 and 4 | OWASP CRS anomaly scoring |
| Rule exclusions | Rule ID or tag plus one target (ARGS:name) on one route; never ctl:ruleEngine=Off | OWASP CRS false-positive tuning |
- Reproduce the false positive and read the matched rule IDs and targets from the ModSecurity audit log before writing any exclusion.
- After the exclusion, confirm the legitimate request passes and that an attack of another class in the same field, the same text in another field and the same field on another route are still blocked.
- Send a real exploit payload in the excluded field and confirm the application encodes or rejects it, because the WAF no longer will.
- Run the change in detection-only or with a higher detection paranoia level first and compare anomaly scores on real traffic.
- For each bot-facing flow, record which OAT it addresses and test the per-account limit, not only the per-address one.
- Applies to
- OWASP CRS 4.30.0 (tested in the owasp/modsecurity-crs:nginx image with ModSecurity and nginx 1.30.5)
WAF and bot-rule operation. Managed signatures cannot establish application authorization or business invariants.
- Part of
- Workflow: Respond to a vulnerability
Learning goals and prerequisites
What you will learn
- Design an exclusion around one false positive without disabling unrelated inspection
- Choose limits for distributed credential stuffing that a per-address rate limit cannot see
- Distinguish pattern filtering from business authorization
Before you start
Platform and security engineers who operate a WAF or CDN rule set and the teams that own the applications behind it
- Distinguish edge decisions, origin behavior, false positives and business abuse.
Refresh first: Maintain an API inventory and prevent gateway bypass
Go to the guided practiceA WAF inspects traffic before the application's full business context is available. It can reduce exposure to recognized patterns and provide temporary containment, but it cannot reliably infer every valid account relationship or repair a transaction race. Treat a virtual patch as a tracked mitigation with a linked application fix and retirement condition.
RASP observes execution inside a running application, while IAST uses instrumentation during testing to find defects. These locations change available context, deployment cost and failure behavior. Evaluate each against a concrete task rather than assuming that adding another category guarantees complete protection.
The synthetic rule model below, with invented rule names, keeps the exercise small; the next section shows the same pattern with the real OWASP CRS. R-A flags an apostrophe in any body field, while R-B flags the literal test marker BAD_TEST in any body field. A legitimate comment, O'Connor's booking, trips the simplistic R-A. Disabling all inspection on the route would also disable R-B and other controls. Excluding only R-A on the comment parameter preserves the rest of the modeled inspection.
Real CRS tuning requires the actual matched rule, target variable, transformation and anomaly score. One rule can inspect many parameters, and multiple matches may contribute to a blocking score. Capture a redacted reproducible legitimate request and compare narrowly scoped exclusions under the installed ruleset. The intended lesson is how to define scope and regression cases, not to recommend removing a named production rule without evidence.
The application must still use safe database APIs and enforce ownership. A WAF cannot infer that authenticated North user N may not cancel South user S’s booking when both requests are syntactically ordinary. Keep an application regression for that relationship even if the edge blocks known injection patterns. Track a temporary virtual patch with its owner, expiry, affected route and application repair.
Bots are the other half of edge work, and signatures rarely see them. In the exercise trace, 40,000 login attempts arrive in an hour from 9,800 residential addresses, none sending more than five, each with a different leaked username and password pair. Every request is well-formed, so no CRS rule matches, and a limit of 20 requests a minute per address never fires. This is OWASP OAT-008 credential stuffing. The controls that bite key on what the attacker reuses or targets, not on the address: failed logins per account and per username, a check of submitted passwords against known breached passwords, and phishing-resistant MFA or passkeys so a correct password is not enough. The MFA and login defense guide at /domains/identity-crypto/authentication/mfa-account-takeover/ covers those controls.
SYNTHETIC RULES, not production CRS identifiers
R-A: any ASCII apostrophe in any body field -> match
R-B: literal BAD_TEST in any body field -> match
Any non-excluded match blocks
Candidate exclusion: R-A only, path=/comments, parameter=comment
T1 /comments comment="O'Connor"
T2 /comments comment="BAD_TEST"
T3 /comments username="O'Connor"
T4 /profile comment="O'Connor"With CRS 4.30 running on ModSecurity at paranoia level 1, a developer-forum comment, wrap it in <script> tags, posted to /comments was blocked with 403. The audit log named three matches on ARGS:comment (rules 941100, 941110 and 941160, all tagged attack-xss) for an anomaly score above the threshold of 5, which rule 949110 enforced. The exclusion below removes the XSS rules from that one parameter on that one route.
Tested with the same requests as the exercise: the legitimate comment passed; a SQL injection string in the same field, the same HTML in a username field and the same comment posted to /profile were all still blocked. A route-wide ctl:ruleEngine=Off also let the legitimate comment through, but it let the SQL injection and the username case through as well. Even the narrow exclusion has a cost: <script>alert(1)</script> in the comment field now passed the WAF, so the application must encode comments on output, and that test belongs in its regression suite.
# Developers post HTML snippets in the comment field of POST /comments. Stop the XSS rules
# from inspecting that one parameter on that one route; every other rule still applies to it,
# and every rule still applies to other parameters and routes.
SecRule REQUEST_FILENAME "@streq /comments" \
"id:10000,\
phase:1,\
pass,\
nolog,\
ctl:ruleRemoveTargetByTag=attack-xss;ARGS:comment"Start with a documented ruleset and a controlled observation phase. CRS groups rules into paranoia levels 1 to 4: level 1 aims for few false positives, and each higher level adds stricter rules that catch more attacks and more legitimate traffic. CRS 4 separates tx.blocking_paranoia_level from tx.detection_paranoia_level, so rules from a higher level can score and log without blocking while you tune. By default it blocks a request whose inbound anomaly score reaches 5 and a response whose score reaches 4. OWASP CRS uses rule matches and anomaly scoring; review which inputs triggered which rules instead of disabling a broad protection because a legitimate request was blocked. Keep a fixture for the legitimate request and one for the intended rejection when introducing an exclusion.
Limit exclusions to the necessary endpoint, parameter and rule where the implementation supports that scope. Verify limits and body inspection behavior for uploads, compressed data and large payloads. Understand what the edge does when inspection fails or a backend is unavailable. Monitor false-positive impact on real tasks, not only the percentage of traffic blocked.
Request smuggling arises when components disagree about request boundaries or framing; the guide at /domains/runtime/edge-defense/http-request-desynchronization/ shows how to test a chain. Use maintained proxy/server implementations, reject ambiguous framing and verify HTTP-version translation. Parser disagreement can undermine routing and WAF assumptions even when the visible request looks ordinary. Run such checks only in an isolated environment because shared connections and caches can affect other traffic.
Shared caches at the edge are a separate boundary: a cache that stores a personalized response under an incomplete key leaks it to the next user without any malicious request. The cache poisoning and deception guide at /domains/runtime/edge-defense/cache-poisoning-deception/ covers cache keys, private responses and how to test them; no WAF rule or threshold repairs a cache key.
Credential stuffing, scraping and inventory hoarding may use syntactically valid authenticated requests. Combine account/tenant/action limits, risk signals and suitable authentication controls with upstream capacity protection. Avoid permanent account lockouts that let an attacker deny access to a victim. Challenges need accessible alternatives and measured failure rates. Test recovery after the abusive pattern stops and keep application permission checks independent of the edge decision.
The OWASP Automated Threats to Web Applications project names 21 kinds of abuse, OAT-001 to OAT-021, which gives teams a shared vocabulary for which flow is attacked and which control applies. Most need application or identity controls rather than WAF signatures.
| OWASP OAT | Abused flow | Controls that limit it |
|---|---|---|
| OAT-008 Credential Stuffing | Login with leaked username and password pairs | Phishing-resistant MFA or passkeys, breached-password checks, per-account and per-network limits |
| OAT-007 Credential Cracking | Guessing passwords for known accounts | Per-account throttling with backoff instead of permanent lockout |
| OAT-019 Account Creation | Bulk sign-ups for spam or bonuses | Verification of a contact channel, per-network limits, delayed privileges |
| OAT-011 Scraping | Bulk collection of pages or API data | Authenticated access, per-account quotas, pagination limits |
| OAT-021 Denial of Inventory | Holding stock or seats without buying | Per-account hold limits and short hold expiry |
| OAT-005 Scalping | Buying limited goods faster than people can | Per-account purchase limits, queueing, verified buyers |
A request that exceeds the inspection body limit may be rejected, partially inspected or passed according to the product and configuration. Test that explicit failure mode. A dashboard showing inspected requests tells you little about traffic skipped by the inspection path.
Bot controls also need a defined unit of abuse. Account recovery, reservation holds and expensive search may require distinct per-account, tenant and action budgets. A challenge or IP limit that blocks a shared office network can harm legitimate users while leaving distributed abuse intact. Measure successful user tasks and recovery after pressure stops.
These outcomes belong to the synthetic model; the CRS test above produced the same pattern for T1 to T4 with real rules.
| Case | Expected result | What it establishes |
|---|---|---|
| T1 with narrow exclusion | Pass these modeled rules | Legitimate comment restored |
| T2 | Block R-B | Other rule on same parameter retained |
| T3 | Block R-A | Other parameter remains inspected |
| T4 | Block R-A | Other endpoint remains inspected |
| Ordinary cross-owner cancellation | Application denies ownership | Business authorization remains independent |
| 40,000 logins from 9,800 addresses, five or fewer each | Per-account and breached-password controls engage; the per-address limit alone never fires | Bot controls key on the abused account, not only the address |
Guided practice
Evaluate the four rule fixtures exactly under the synthetic model, then decide how to stop the login abuse described below.
Supplied scenario material
Exclusion E1: disable every rule for /comments.
Exclusion E2: disable R-A only for /comments parameter comment.
B1 POST /login: 40,000 attempts in one hour; 9,800 distinct client addresses; at most 5 per address.
B2 Each attempt uses a different username and password pair; 61% of usernames exist; 0.4% of attempts succeed.
B3 Current controls: WAF with CRS at paranoia level 1; rate limit 20 requests per minute per address; no MFA.
A normal authenticated request by N attempts to cancel a booking owned by S.1. Compare exclusions
Apply E1 and E2 to T1–T4 and explain which preserves the intended test rejection.
Expected result and reasoning
E1 lets T1, T2 and T3 through on
/commentsand still blocks T4. E2 lets only T1 through; T2, T3 and T4 remain blocked. E2 preserves the modeledBAD_TESTrejection and other parameter/route checks.2. Choose the bot controls
Use B1 to B3 to explain why the current controls miss the attack and which controls would limit it.
Expected result and reasoning
Every request is syntactically normal, so CRS has nothing to match, and no address exceeds the per-address limit. This is credential stuffing (OAT-008). Add per-account and per-username failure limits with backoff rather than permanent lockout, check passwords against breached-password lists, require phishing-resistant MFA or passkeys, and treat the accounts with successful logins as compromised: reset their sessions and credentials.
3. Keep the application test
Decide whether passing edge inspection authorizes N’s cancellation.
Expected result and reasoning
No. The application must check the booking owner or delegated permission and deny N under the stated ownership model. Pattern inspection has no supplied authority to grant that business action.
4. Define release evidence
Specify positive and negative checks for the final repair.
Expected result and reasoning
Retain T1 as a legitimate successful request and T2 to T4 as rejection controls under the model. Replay a distributed login test from many addresses and confirm the per-account limit and MFA stop it while a real user still signs in. Verify an owner can cancel and a non-owner cannot.
The rule evaluation uses the synthetic model; the CRS exclusion on this page shows the same scoping with real rules.
Save a draft on this device to resume later. Automatic saving begins after you choose Save. Drafts are never sent by this application; use synthetic data.
No draft saved on this device yet.
Run a local project
Download and unzip a starter with failing tests and a step-by-step README, edit the named exercise file, and run the tests until they pass. The reference solution is a separate download, so you can attempt the repair before you see it.
Repair a real proxy and shared cache
Reproduce a private-response leak, repair cache policy and verify HTTP framing with real origin/proxy servers and raw socket requests.
In the extracted folder, run:
node observe.mjs
node --test exercise.test.mjs12 regression tests · The starter intentionally fails some cases.
Opens in a dev container (VS Code Dev Containers or GitHub Codespaces) with a pinned image.
Part of the learning path Browser and API boundaries
Actual HTTP/1.1 loopback traffic and a narrow cache contract; it is not a full HTTP cache or a test of your production intermediary chain.
Sources & further reading
The standards, specifications and documentation behind the guidance above. Each is the evidence for a claim on this page; consult it for detail beyond the defaults and versions listed here.
Reference information
- Tags
- Denial of service and resource limitsAccount takeoverAuthentication and sessionsWAFs and edge defenses
- SDLC phase
- Maintenance
- ASVS 5.0.0 requirements
- v5.0.0-2.4.1 · v5.0.0-6.3.1