Skip to content

v0.2.0 — CRS exclusions, multipart headers, chain semantics

Choose a tag to compare

@raffaelschneider raffaelschneider released this 22 Aug 09:32
· 17 commits to main since this release
d72bdaa

Adds the OWASP CRS compatibility work reported against 0.1.2, and fixes a chain-evaluation bug that produced false positives.

This is a minor bump because rule evaluation changes. Consumers pinning "0.1.x" will not pick it up automatically, which is deliberate — see Upgrading below.

Added

  • SecRuleUpdateTargetById (#17) — the directive CRS deployments use to tune away false positives without disabling whole rules. Supports !TARGET exclusions, target appends, the three-argument replace form, and single IDs or ID ranges. Applied after the full ruleset is compiled, so it works whether the directive appears before or after the rule it targets — CRS exclusion files are conventionally included last.

    SecRuleUpdateTargetById 942100 "!ARGS:password"
    SecRuleUpdateTargetById 942000-942999 "!ARGS:token"

    Closes zentinelproxy/zentinel#342.

  • MULTIPART_PART_HEADERS is populated (#17). The variable was registered in 0.1.3, which fixed ruleset loading — but nothing ever filled the collection, so CRS's REQUEST-922-MULTIPART-ATTACK rules loaded and then matched nothing on every request. Part headers are now recorded from the multipart parser, keyed by part name. Part content is deliberately excluded, so an uploaded text file containing a line like Content-Type: application/x-httpd-php cannot trip a multipart rule.

    Closes zentinelproxy/zentinel#341.

Fixed

  • Chained rules no longer fire on a partial match (#18). A chain starter executed its disruptive action as soon as its own condition matched, without waiting for the remaining links. Since CRS leans heavily on chains, this produced false positives — blocking traffic the ruleset was written to allow.

    Underneath it, continuation rules were filed under the wrong phase. They carry no phase action (ModSecurity does not permit one) and the phase was derived per rule, defaulting to phase 2 — so a phase:1 chain had its continuation stored elsewhere, could never complete, and left the continuation evaluable as a standalone rule. Fixing only the first bug turns the false positive into a false negative, so both landed together.

  • Benchmarks execute rules in the measured section (#16). The throughput and transaction comparisons stopped after phase 1 while the only detection rule was phase:2, so neither engine evaluated anything and the comparison measured transaction setup overhead. The benchmarks now assert, before measuring, that attack input is blocked and clean input is not. Reported in #15.

    Corrected figures against libmodsecurity 3.0.16: 4.2x on clean requests, 11.5x on SQLi detection, 8.9x on body processing, 676K req/s clean throughput. Re-measured again in #20 after the changes above, which cost about 15% — correct multipart handling and chain evaluation are not free.

Upgrading

Chain evaluation changes which requests are blocked, in both directions: chains that fired on a partial match no longer do, and phase:1 chains that could never complete now can. Re-test your ruleset against representative traffic before deploying.

zentinel-modsec = "0.2.0"

Still open

ctl: actions parse but are not executed (#19), and the JSON and XML body processors have not been audited. Both are tracked in zentinelproxy/zentinel#340.