Download the PHP package flowd/phirewall-preset-owasp-crs without Composer

On this page you can find all versions of the php package flowd/phirewall-preset-owasp-crs. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.

FAQ

After the download, you have to make one include require_once('vendor/autoload.php');. After that you have to import the classes with use statements.

Example:
If you use only one package a project is not needed. But if you use more then one package, without a project it is not possible to import the classes with use statements.

In general, it is recommended to use always a project to download your libraries. In an application normally there is more than one library needed.
Some PHP packages are not free to download and because of that hosted in private repositories. In this case some credentials are needed to access such packages. Please use the auth.json textarea to insert credentials, if a package is coming from a private repository. You can look here for more information.

  • Some hosting areas are not accessible by a terminal or SSH. Then it is not possible to use Composer.
  • To use Composer is sometimes complicated. Especially for beginners.
  • Composer needs much resources. Sometimes they are not available on a simple webspace.
  • If you are using private repositories you don't need to share your credentials. You can set up everything on our site and then you provide a simple download link to your team member.
  • Simplify your Composer build process. Use our own command line tool to download the vendor folder as binary. This makes your build process faster and you don't need to expose your credentials for private repositories.
Please rate this library. Is it a good library?

Informations about the package phirewall-preset-owasp-crs

Phirewall OWASP Core Rule Set

OWASP Core Rule Set (CRS) support for flowd/phirewall, the PSR-7/PSR-15 PHP firewall.

This package provides two things:

  1. The ModSecurity SecRule engine (Flowd\PhirewallPresetOwaspCrs\Engine\) - a parser and evaluator for ModSecurity-style SecRule directives, usable with any ruleset. It was extracted from the core flowd/phirewall package in 0.6.
  2. Ready-made CRS presets - a pre-filtered, per-paranoia-level snapshot of the CRS request rules, exposed as Config overlays. Evaluation uses CRS-style anomaly scoring: every matching rule contributes its severity score, and the request is blocked once the accumulated score reaches the threshold.
    • Blocklist preset - block every request whose anomaly score reaches the threshold.
    • Fail2Ban preset - block scoring requests and additionally ban a client key that keeps scoring.

Installation

Usage

Presets are ConfigLayers. Apply them onto your existing configuration with Config::with() (the preset never brings its own cache; your Config's cache, event dispatcher and clock stay in charge):

Want to also ban probing clients, not just block their scoring requests? Use the fail2ban preset. It blocks every request whose anomaly score reaches the threshold, like the blocklist, and additionally bans the client key (the IP by default) once it produced threshold such requests within period seconds, so all further traffic from that key is blocked until the ban expires:

Both presets accept an anomalyThreshold (default 5, the CRS standard), a configure closure for tuning and a PSR-3 logger for match logging:

For manual wiring (custom rule name, enabling/disabling single CRS rule ids), get the raw rule set:

Presets::crsVersion() returns the bundled upstream release tag.

Anomaly scoring

Like upstream CRS, every matching rule contributes its severity score to the request's anomaly score, and the request is blocked once the accumulated score reaches the threshold (score >= threshold):

Severity Score
CRITICAL 5
ERROR 4
WARNING 3
NOTICE 2

The default threshold is 5, the CRS standard inbound threshold. In the bundled CRS snapshot most rules are CRITICAL and still block on their own; WARNING rules (for example the 942430 restricted-character checks, a classic source of false positives) no longer block alone - two of them together do. anomalyThreshold: 1 restores block-on-first-match behaviour; raising the threshold above 5 substantially increases the risk of attacks passing.

Details:

A request blocked on its anomaly score - or by a rule-level fail-closed outcome (a capped variable, an oversized value, or a PCRE subject error) - carries owasp_anomaly_score, owasp_anomaly_threshold, owasp_rule_ids (comma-separated) and owasp_rule_id (first match); it also carries msg, owasp_matched_variable (the matched target, e.g. REQUEST_HEADERS:User-Agent - the member name keeps the casing the client sent), owasp_matched_value (the value the rule fired on, readable so the match can be understood; sanitized, length-bounded and [redacted] for credential targets such as cookie values and Authorization-type headers) and owasp_log_data when the first matching rule provides them, and owasp_fail_closed on a fail-closed block (there owasp_matched_variable names the variable that failed closed). With Config::enableDiagnosticsHeaders() such blocked responses carry X-Phirewall-Owasp-Rule (up to 10 rule ids, then ,+N) and X-Phirewall-Owasp-Score (score/threshold). A block from an engine-internal fault under a fail-closed policy (useFailOpen(false)) instead carries only owasp_anomaly_threshold and owasp_fail_closed - no scoring metadata or diagnostics headers, since no rule scored.

Excluding parameters from rules (false positives)

Marketing and tracking parameters (utm_*, fbclid, ...) regularly carry values that look like attack payloads to CRS rules. Instead of disabling whole rules, exclude the parameter from inspection - globally, per rule id (CRS SecRuleUpdateTargetById style) or per rule tag:

Selector forms: bare variable (ARGS), exact name (ARGS:utm_source) or name pattern (ARGS:/^utm_/). Header names match case-insensitively, argument and cookie names case-sensitively. Excluding a parameter's value usually wants its name excluded too (the ARGS_NAMES twin), since rules also inspect parameter names.

The same methods exist on CoreRuleSetMatcher (queued until the rules load) and are reachable through the presets' configure: closure. Exclusions are runtime tuning: they never enter the compiled-data cache artifact, and they cannot lift the collection cap - a request padded past the cap still fails closed.

A tag that no loaded rule carries makes a tag-scoped exclusion a silent no-op - the classic case is a CRS 3 tag (OWASP_CRS/WEB_ATTACK/SQL_INJECTION) against the CRS 4 snapshot, whose tags are attack-sqli and friends. With a PSR-3 logger on the matcher, such a tag logs a warning once the rules are loaded; this covers excludeTargetByTag() and the tag-scoped CRS exclusion syntax alike.

Limitation: exclusions do not rewrite the raw QUERY_STRING/REQUEST_URI values, so the few rules inspecting those (920260, 920540, 920460 inspect REQUEST_URI; 931110 inspects QUERY_STRING) still see the full string. If one of them false-positives, use disable($ruleId), a manipulator, or a bare-variable exclusion naming the variable that rule actually inspects - excludeTargetById(920260, 'REQUEST_URI') or excludeTargetById(931110, 'QUERY_STRING'). A selector naming a variable the rule does not target is accepted but silently does nothing.

Conditional exclusions: validate the value first

Every exclude method accepts a when: condition - the selected entry is only excluded while the condition approves its value. That turns a blanket exclusion into a validated one: a parameter is skipped when it provably carries a legitimate value and stays fully inspected otherwise:

The condition receives (string $variable, ?string $name, string $value, ServerRequestInterface $request) - the same argument order as a manipulator - and returns true to exclude; a closure declaring fewer parameters ignores the rest. The request enables context-dependent validation (per-host issuers, path-scoped rules, comparing against another header). Implement TargetExclusionConditionInterface for a reusable validator. Validate strictly - verify the signature, parse the full format: everything the condition approves is invisible to the rules in scope, and a shape-only check ("looks like a JWT") invites attackers to wrap payloads in that shape. Exceptions thrown by a condition propagate like manipulator exceptions and follow the failure policy (useFailOpen()).

CRS rule-exclusion syntax

applyRuleExclusions() and applyRuleExclusionsFromFile() - on CoreRuleSet and CoreRuleSetMatcher (queued until the rules load, validated eagerly) and thus reachable through the presets' configure: closure - accept the CRS rule-exclusion syntax, so existing ModSecurity tuning files can be reused:

Supported forms: SecRuleRemoveById (ids and from-to ranges), SecRuleRemoveByTag (exact tag, not a regex), SecRuleUpdateTargetById / SecRuleUpdateTargetByTag (negated !TARGET removals only), and runtime SecRule exclusions with ctl:ruleRemoveById, ctl:ruleRemoveByTag, ctl:ruleRemoveTargetById or ctl:ruleRemoveTargetByTag. Anything the engine cannot evaluate faithfully fails eagerly with an InvalidArgumentException instead of arming a weaker or dead exclusion: chained rules, unsupported condition operators or variables, target additions and malformed directives all throw. Unknown directives (SecMarker, ...) and other ctl: options (ctl:ruleEngine, ...) are skipped. Like every exclusion this is runtime tuning and never enters the compiled-data cache.

A runtime exclusion's condition can only pattern-match (@rx and friends), so it can check that a value looks like a JWT but not that it is one - an attacker can wrap a payload in the approved shape. When the value can be validated in PHP, prefer a conditional exclusion (when:) that verifies the signature; see above.

Manipulators (advanced, weakens detection)

A manipulator transforms collected values before rules match against them - the escape hatch for cases where excluding a whole parameter is too broad. Returning an empty string removes the value from inspection:

Closure manipulators receive the request as an optional fourth argument (string $variable, ?string $name, string $value, ServerRequestInterface $request) - the same order as a when: exclusion condition; declare it when the transformation depends on request context. The three-parameter RequestValueManipulatorInterface is unchanged.

Warning: whatever a manipulator removes or rewrites is invisible to every rule it applies to - including real attack payloads hidden inside the removed content. Prefer target exclusions; keep manipulators as narrow as possible. Exceptions thrown by a manipulator propagate to the caller.

Manipulators run after exclusions. Global manipulators are applied once per variable per request and shared across all rules; per-rule manipulators specialize from that shared result.

Logging

Pass a PSR-3 logger to either preset (or to CoreRuleSetMatcher) to log every rule match at info level - including matches on requests that stay below the threshold and pass. Those sub-threshold entries are the tuning signal: watch them to find false-positive patterns (a utm_content value hitting 942431, say) before scores ever accumulate to a block, then add a target exclusion. Blocked requests additionally log a warning with the total score, threshold and all matched rule ids.

The per-match log context carries rule_id, severity, anomaly_score, paranoia_level, matched_variable (e.g. ARGS:utm_content), matched_value (the value the rule fired on; [redacted] for credential targets), msg, fail_closed, method, path and log_data - the rule's CRS logdata: template expanded with the matched data (%{TX.0}, %{MATCHED_VAR_NAME}, %{MATCHED_VAR}); the warning context carries total_score, anomaly_threshold, rule_ids, fail_closed, method and path. Attacker-controlled context values (matched_variable, matched_value, path, log_data) are sanitized (control characters stripped) and length-bounded before they reach the log line. When the matched target is a credential - a cookie or an Authorization, Cookie, Proxy-Authorization, X-Api-Key or X-Auth-Token header - its value (and captures) is replaced with [redacted] in matched_value, log_data and the owasp_log_data/owasp_matched_value metadata; the target name is kept for tuning.

Using the SecRule engine directly

The engine can load any ModSecurity-style ruleset, not just the bundled CRS:

The engine implements a pragmatic subset of ModSecurity; see the table below.

Paranoia levels

Like upstream CRS, paranoia levels are cumulative: ParanoiaLevel::Level2 activates all level 1 and level 2 rules. Level 1 is designed to be safe for most applications; higher levels detect more but produce more false positives. In this snapshot almost every rule at every level is CRITICAL and blocks on its own at the default threshold; only the handful of WARNING rules merely accumulate score. Raising the paranoia level therefore mostly adds more CRITICAL rules, so expect more blocking (and more false positives), not just more accumulated score. Start with level 1, watch the match log with a higher level against your real traffic, add exclusions for the false positives you find, then raise the level.

Skipping the per-request parse

Parsing the CRS rule files costs several milliseconds and, under PHP-FPM, would run on every request. Both presets therefore load lazily: the parse happens on the first evaluated request. To also skip that first parse per process, give your Config a compiled-data cache (phirewall 0.9 or newer) - the parsed rules are then served from an OPcache-backed artifact and re-parsed only when a rule file changes:

For manual wiring use the lazy factory; rule toggles before the first request are queued and applied once the rules are loaded:

A matcher constructed with an already parsed CoreRuleSet keeps the eager behaviour and ignores the cache.

What is included (and what is not)

Phirewall's SecRule engine implements a pragmatic subset of ModSecurity. The import process therefore ships only the CRS rules that the engine can evaluate faithfully:

Filter Effect
Request phase only RESPONSE-*.conf files and exclusion templates are skipped
Blocking rules only Rules without a deny/block action (initialization, control flow) are dropped; kept rules contribute their severity score to the anomaly total
No chains Chained rules are dropped entirely; keeping only a chain's first condition would over-block
Supported operators @rx, @contains, @streq, @beginsWith, @endsWith, @pm, @pmFromFile; everything else (@detectSQLi, @validateByteRange, ...) is dropped
Supported targets REQUEST_URI, REQUEST_METHOD, QUERY_STRING, ARGS, ARGS_NAMES, REQUEST_COOKIES, REQUEST_COOKIES_NAMES, REQUEST_HEADERS, REQUEST_HEADERS_NAMES, REQUEST_FILENAME - bare or with a named selector (REQUEST_HEADERS:User-Agent, !ARGS_NAMES:/^utm_/); rules whose positive targets are all unsupported (XML:/*, REQUEST_BODY, ...) are dropped

Further engine differences to be aware of:

resources/rules/manifest.json records the imported release, per-level rule counts and how many rules were dropped per reason; imports since 0.5 also record per-severity rule counts (ruleCountsBySeverity).

This package hardens a PHP application but is not a replacement for a full WAF deployment of the CRS.

Updating the bundled rules

The command downloads the release tarball, filters the rules as described above, splits them per paranoia level into resources/rules/*.plN.conf, copies referenced .data files and writes manifest.json. Prefer an explicit --tag; a bare import of "latest" warns because it is not reproducible. --sha256 verifies the downloaded tarball before extraction (the computed hash is always printed so it can be pinned), and GITHUB_TOKEN, if set, is sent only to the GitHub API, never with the tarball download.

The scheduled CRS Update GitHub Actions workflow runs the import weekly and opens a pull request when a new CRS release was imported. The test suite runs in that pull request, where the required CI passed check gates the merge, so an import that breaks the suite still surfaces as a reviewable PR. Because the PR is created with the workflow GITHUB_TOKEN, its CI checks must be triggered manually once (close and reopen the PR). Releases of this package are tagged manually after review.

Development

PHPUnit test suites: Unit (preset logic), Engine (the SecRule engine), ShippedRules (the committed CRS import), and Integration (.phpt end-to-end).

The ShippedRules PHPUnit test suite validates the committed import output (manifest consistency, every rule parses, smoke checks against known attacks). It also runs one behavioral test per shipped rule: a verified attack payload is fed through the engine and asserted to trigger exactly that rule id. Rules that cannot be triggered through a normalized PSR-7 request (for example a newline in the request filename) are listed as documented exceptions, so no rule is silently untested.

The payloads live in tests/Fixtures/rule-payloads.php and are regenerated after an import with:

The generator derives a triggering payload for each rule from its own operator (sampling the @rx regex, picking phrases for @pm/@pmFromFile) and only keeps payloads it has verified fire the rule in isolation.

Versioning

This package follows Semantic Versioning. It is still pre-1.0, so per the SemVer 0.x rules a breaking change may land in a minor release; every such change is called out in CHANGELOG.md.

A change is breaking when it alters either:

The bundled OWASP CRS rules are data, not API: updates to them ride in minor or patch releases without counting as breaking, even though a new or revised rule can change which requests are blocked (traffic that previously passed may now match, and vice versa). Rule updates are always recorded in CHANGELOG.md, so review it before upgrading. Presets::crsVersion() reports the bundled upstream release tag.

Supported PHP versions: >=8.2, tested on 8.2, 8.3, 8.4 and 8.5. Security fixes are provided for the latest minor release only (see SECURITY.md).

License

The package code is dual-licensed under LGPL-3.0-or-later and a proprietary license, like flowd/phirewall itself.

The bundled OWASP CRS rules under resources/rules/ are a separate work: Copyright (c) the OWASP CRS project, licensed under Apache License 2.0 (see resources/rules/LICENSE and, when present, resources/rules/NOTICE). They are a filtered subset of upstream CRS, reformatted per paranoia level. The Apache-2.0 terms govern the rules regardless of which license you use for the package code - choosing the proprietary option does not relicense them.


All versions of phirewall-preset-owasp-crs with dependencies

PHP Build Version
Package Version
Requires php Version >=8.2
flowd/phirewall Version >=0.9 <1.0
psr/log Version ^2.0 || ^3.0
Composer command for our command line client (download client) This client runs in each environment. You don't need a specific PHP version etc. The first 20 API calls are free. Standard composer command

The package flowd/phirewall-preset-owasp-crs contains the following files

Loading the files please wait ...