fix(waf): promote 920440 chain-safely, not on its chain starter (round 2)

Owner decision. `SecRuleUpdateActionById 920440 "ctl:ruleEngine=On"` ran on
920440's chain STARTER (`REQUEST_BASENAME "@rx \.([^.]+)$"`), which matches
every URL with a file extension, so every *.php/*.css request was evaluated
with the engine On: full CRS anomaly enforcement (949110) and 200002, in
detect_only mode too.

Replaced by `SecRule TX:EXTENSION "@within %{tx.restricted_extensions}"
... ctl:ruleEngine=On` (id 992920440, phase 1, after 920440). CRS 4.25.0
sets tx.extension in 920440's first link (REQUEST-920...conf:667, the
coreruleset version coraza-spoa v0.7.1 pins). Live, both modes:
.bak/.sql/.save/.old/.swp probes still 403; benign .php/.css 200; a
942100 SQLi on /x.php and the onclick / "select ... union" admin saves are
no longer 403 in detect_only.

test-waf-body-inspection.py: 19 tests (2 new, repo-checkout only).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
shadowdaoandClaude Opus 5.5 committed 2026-10-09 14:00:22 -07:00
1 parent cc0652e0c0
commit 234029c249
2 files changed
+48 -10

No files matched your search

+19 -10
View File
@@ -59,17 +59,26 @@ SecRuleUpdateActionById 944100-944300 "ctl:ruleEngine=On"
# single GCP-hosted scanner. Zero false positives observed; standard
# WP/WooCommerce/Divi/HPR URLs do not end in these extensions.
# ---------------------------------------------------------------------------
# WARNING (measured 2026-10-09, Coraza v3.7.0 + CRS 4.25.0, in-process AND
# live behind haproxy): 920440 is a CHAIN whose first link is
# HOW IT IS PROMOTED (changed 2026-10-09, haproxy-manager-base 2026.10.2).
# This used to be `SecRuleUpdateActionById 920440 "ctl:ruleEngine=On"`, which
# was WRONG: 920440 is a CHAIN whose first link is
# `REQUEST_BASENAME "@rx \.([^.]+)$"` -- it matches EVERY URL with a file
# extension. Coraza runs the appended ctl:ruleEngine=On when that first link
# matches, even though the chain then fails. So on any *.php / *.css / ...
# request the engine is On for the rest of the transaction: 949110 enforces
# the whole CRS anomaly score (a 942100 SQLi GET is 403 on /x.php, 200 on /x)
# and 200002 denies body-parse errors -- in detect_only mode too, because
# SecRuleEngine DetectionOnly cannot override a runtime ctl. Not changed here
# (it would loosen live blocking fleet-wide); see waf-body-hardening-report.md.
SecRuleUpdateActionById 920440 "ctl:ruleEngine=On"
# extension -- and Coraza runs actions appended to a chain starter when that
# first link matches, even though the chain then fails. Measured (Coraza
# v3.7.0 + CRS 4.25.0, in-process and live): every *.php / *.css / ...
# request ran with the engine On, so the whole CRS anomaly score (949110) and
# 200002 enforced there -- in detect_only mode too, since SecRuleEngine
# DetectionOnly cannot override a runtime ctl.
#
# Now: 920440 itself is untouched (it still sets tx.extension in its first
# link and adds the anomaly score in its second), and this separate phase-1
# rule, which runs after it, flips the engine only when the extension really
# is in tx.restricted_extensions -- the original intent. Removing 920440
# (break-glass, a per-host exception) also removes tx.extension, so this rule
# goes quiet with it. Id 992920440 is outside WHP's generated range
# (990000000-991999999).
SecRule TX:EXTENSION "@within %{tx.restricted_extensions}" \
"id:992920440,phase:1,pass,nolog,t:none,t:lowercase,ctl:ruleEngine=On"
# ---------------------------------------------------------------------------
# 930130 — Restricted File Access Attempt