From 66c66eb923e4f7e953b6a106dc1cd85dff74be7a Mon Sep 17 00:00:00 2001 From: Felipe Zipitria Date: Wed, 14 Oct 2026 07:45:36 -0300 Subject: [PATCH 1/3] docs: add configuration settings reference and sync with CRS 4.30.0 Add a reference page listing every crs-setup.conf setting with its default, enforcing rule, and when to change it. Update passages that still showed the single phase:2 SecDefaultAction: CRS 4.28.0 sets a default for phases 1-5. Note that UTF-8 validation is now on by default, document the _method override toggle (900210/920650), and point out that application/*+json and AWS JSON content types are auto-allowed since 4.30.0 (rules 901360/901370). Bump crs_latest_release to 4.30.0. Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_016dPLzCS17k6fiP7qf35iZi --- config/_default/config.toml | 2 +- .../1-getting-started/1-1-crs-installation.md | 2 + .../2-3-false-positives-and-tuning.md | 4 + .../2-5-configuration-settings.md | 123 ++++++++++++++++++ content/3-about-rules/creating.md | 2 +- .../5-1-self-contained-mode.md | 26 ++-- 6 files changed, 148 insertions(+), 11 deletions(-) create mode 100644 content/2-how-crs-works/2-5-configuration-settings.md diff --git a/config/_default/config.toml b/config/_default/config.toml index 429043c1..6ef0553b 100644 --- a/config/_default/config.toml +++ b/config/_default/config.toml @@ -45,7 +45,7 @@ home = ['html', 'rss', 'print'] #custom_css = ["css/foo.css", "css/bar.css"] # CRS related parameters - crs_latest_release = "4.28.0" + crs_latest_release = "4.30.0" crs_dev_branch = "main" crs_install_dir = "/etc/crs4" diff --git a/content/1-getting-started/1-1-crs-installation.md b/content/1-getting-started/1-1-crs-installation.md index 66b62e34..0a48a649 100644 --- a/content/1-getting-started/1-1-crs-installation.md +++ b/content/1-getting-started/1-1-crs-installation.md @@ -95,6 +95,8 @@ Other aspects of ModSecurity, particularly engine-specific parameters, are contr In many scenarios, the default example CRS configuration will be a good enough starting point. It is, however, a good idea to take the time to look through the example configuration file *before* deploying it to make sure it's right for a given environment. +A reference of every setting in the file, with defaults and guidance on when to change them, is in [configuration settings]({{% ref "2-5-configuration-settings.md" %}}). + {{% notice style="warning" icon="shield-halved" %}} In particular, _Response_ rules are enabled by default. You must be aware that you may be vulnerable to RFDoS attacks, depending on the responses your application is sending back to the client. You could be vulnerable, if your responses from your application can contain user input. If an attacker can submit user input that is returned as part of a response, the attacker can craft the input in such a way that the response rules of the WAF will block responses containing that input _for all_ clients. For example, a blog post might no longer be accessible because of the contents of a comment on the post. See [this blog post](https://blog.sicuranext.com/response-filter-denial-of-service-a-new-way-to-shutdown-a-website/) about the problems you could face. There is an [experimental scanner](https://github.com/edoardottt/RFDos-Scanner) that uses [nuclei](https://github.com/projectdiscovery/nuclei?tab=readme-ov-file#install-nuclei) to find out if are affected. So if diff --git a/content/2-how-crs-works/2-3-false-positives-and-tuning.md b/content/2-how-crs-works/2-3-false-positives-and-tuning.md index 2ecc6b56..3eb88def 100644 --- a/content/2-how-crs-works/2-3-false-positives-and-tuning.md +++ b/content/2-how-crs-works/2-3-false-positives-and-tuning.md @@ -365,6 +365,10 @@ SecRule REQUEST_URI "@beginsWith /webapp/login.html" \ ModSecurity/Coraza relies on the Content Type to correctly parse a request body, allowing additional Content Types may result in a complete WAF bypass if the correct body parser has not been activated. {{% /notice %}} +{{% notice style="tip" icon="code-branch" %}} +Since CRS 4.30.0, no exclusion is needed for `application/*+json` Content Types (for example `application/ld+json` or `application/vnd.api+json`) or for the AWS `application/x-amz-json-1.0` and `application/x-amz-json-1.1` types. Rules 901370 and 901360 allow them and switch on the JSON body processor automatically. See [configuration settings]({{% ref "2-5-configuration-settings.md" %}}) for the full list of allowed Content Types. +{{% /notice %}} + {{% notice style="tip" icon="code-branch" %}} It's possible to write a conditional rule exclusion that tests something other than just the request URI. Conditions can be built which test, for example, the source IP address, HTTP request method, HTTP headers, and even the day of the week. diff --git a/content/2-how-crs-works/2-5-configuration-settings.md b/content/2-how-crs-works/2-5-configuration-settings.md new file mode 100644 index 00000000..42b294e2 --- /dev/null +++ b/content/2-how-crs-works/2-5-configuration-settings.md @@ -0,0 +1,123 @@ +--- +title: Configuration Settings +weight: 25 +disableToc: false +chapter: false +--- + +> CRS is configured through `crs-setup.conf`. This page lists every setting in that file, its default, and when to change it. It is a reference; the concepts behind the main settings are explained in the other pages of this chapter. + +## How Settings Work + +The file `crs-setup.conf.example` ships with every release. Copy it to `crs-setup.conf` and include it *before* the rule files, as described in the [installation guide]({{% ref "1-1-crs-installation.md" %}}). + +Almost every setting is a `SecAction` rule with an ID in the `900xxx` range that sets a transaction variable with `setvar`. Most of these rules are commented out: the rule file `REQUEST-901-INITIALIZATION.conf` applies the default value whenever a variable has not been set. To change a setting, uncomment its rule in `crs-setup.conf` and edit the value. + +{{% notice note %}} +`crs-setup.conf` configures CRS only. Engine settings such as `SecRuleEngine`, `SecRequestBodyAccess`, or the audit log live in the engine's own configuration file (`modsecurity.conf` or `coraza.conf`), which must be loaded first. +{{% /notice %}} + +{{% notice style="tip" icon="upload" %}} +When upgrading CRS, diff your `crs-setup.conf` against the new `crs-setup.conf.example`. New settings are added over time and defaults change. Rule `900990` records the setup file version in `tx.crs_setup_version`. If the variable is missing, rule 901001 denies every request with a 500 status, which is how CRS detects a missing or wrongly ordered setup file. +{{% /notice %}} + +## Default Actions + +Plain `SecDefaultAction` directives at the top of the file set the engine behavior for rules that don't specify their own. Since CRS 4.28.0 a default is set for every phase, so phases 3 to 5 (response and logging) log the same way as the request phases: + +```apache +SecDefaultAction "phase:1,log,auditlog,pass" +SecDefaultAction "phase:2,log,auditlog,pass" +SecDefaultAction "phase:3,log,auditlog,pass" +SecDefaultAction "phase:4,log,auditlog,pass" +SecDefaultAction "phase:5,log,auditlog,pass" +``` + +The default `pass` action is what makes anomaly scoring work: individual rules only add to the score and the blocking decision is taken at the end of the phase. Replace `log` with `nolog` to write alerts only to the audit log. Changing `pass` to `deny` switches to [self-contained mode]({{% ref "5-1-self-contained-mode.md" %}}), which is discouraged. + +## Anomaly Scoring + +See [anomaly scoring]({{% ref "2-1-anomaly_scoring" %}}) for the concepts. + +| Rule | Variable | Default | Purpose | +|------|----------|---------|---------| +| 900100 | `tx.critical_anomaly_score` | 5 | Score added by a critical severity rule | +| 900100 | `tx.error_anomaly_score` | 4 | Score added by an error severity rule | +| 900100 | `tx.warning_anomaly_score` | 3 | Score added by a warning severity rule | +| 900100 | `tx.notice_anomaly_score` | 2 | Score added by a notice severity rule | +| 900110 | `tx.inbound_anomaly_score_threshold` | 5 | Request is blocked when its score reaches this value | +| 900110 | `tx.outbound_anomaly_score_threshold` | 4 | Response is blocked when its score reaches this value | +| 900115 | `tx.reporting_level` | 4 | Verbosity of the phase 5 anomaly score reporting rules, 0 (off) to 5 (every request) | +| 900120 | `tx.early_blocking` | 0 | Set to 1 to block at the end of phase 1 or 3 instead of waiting for phase 2 or 4 | + +## Paranoia Levels + +See [paranoia levels]({{% ref "2-2-paranoia_levels.md" %}}) for the concepts. + +| Rule | Variable | Default | Purpose | +|------|----------|---------|---------| +| 900000 | `tx.blocking_paranoia_level` | 1 | Rules up to this level contribute to the blocking score | +| 900001 | `tx.detection_paranoia_level` | same as blocking | Rules up to this level run and are logged, but only contribute to a separate detection score | + +## HTTP Policy + +These settings feed the protocol enforcement rules in `REQUEST-920-PROTOCOL-ENFORCEMENT.conf`. Lists use a delimiter around each entry so that the `@within` operator matches exactly: pipes for content types and charsets, slashes for file extensions and headers, spaces for methods and versions. + +| Rule | Variable | Default | Purpose | +|------|----------|---------|---------| +| 900200 | `tx.allowed_methods` | `GET HEAD POST OPTIONS` | HTTP methods accepted by rule 911100 | +| 900210 | `tx.allow_method_override_parameter` | 0 | Set to 1 to allow the `_method` request parameter used by some frameworks to override the HTTP method. Rule 920650 blocks it at paranoia level 2 and above | +| 900220 | `tx.allowed_request_content_type` | see below | Request content types accepted by rule 920420 | +| 900230 | `tx.allowed_http_versions` | `HTTP/1.0 HTTP/1.1 HTTP/2 HTTP/2.0 HTTP/3 HTTP/3.0` | Protocol versions accepted by rule 920430. HTTP/0.9 is no longer supported | +| 900240 | `tx.restricted_extensions` | long list | File extensions blocked by rule 920440: backups, configuration files, keys, logs, shell scripts, and similar | +| 900250 | `tx.restricted_headers_basic` | see below | Request headers blocked by rule 920450 at paranoia level 1 | +| 900255 | `tx.restricted_headers_extended` | `/accept-charset/` | Additional headers blocked by rule 920451 at paranoia level 2 | +| 900280 | `tx.allowed_request_content_type_charset` | `utf-8`, `iso-8859-1`, `iso-8859-15`, `windows-1252` | Charsets accepted in the `Content-Type` header by rule 920480 | + +### Allowed Content Types + +The default list is: + +``` +|application/x-www-form-urlencoded| |multipart/form-data| |text/xml| |application/xml| |application/soap+xml| |application/json| +``` + +In addition, two rules in `REQUEST-901-INITIALIZATION.conf` extend this list at runtime since CRS 4.30.0, and switch on the JSON body processor in the same rule so there is no bypass risk: + +- **Rule 901370** allows any `application/*+json` type (RFC 6839 structured syntax suffix), for example `application/ld+json`, `application/problem+json`, or `application/vnd.api+json`. +- **Rule 901360** allows the AWS types `application/x-amz-json-1.0` and `application/x-amz-json-1.1`. + +To turn either off, remove the rule with `ctl:ruleRemoveById` the same way as any other CRS rule. Content types without a corresponding body processor, such as `text/plain` or `application/octet-stream`, must never be added to the list without also forcing a body processor; see example 9 in [false positives and tuning]({{% ref "2-3-false-positives-and-tuning.md" %}}). + +### Restricted Headers + +The default basic list blocks headers that let a client alter how the request is processed: `content-encoding`, `proxy`, `lock-token`, `content-range`, `if`, `x-http-method-override`, `x-http-method`, `x-method-override`, `x-middleware-subrequest`, and `expect`. The `expect` entry blocks `Expect`-based request smuggling. .NET's `HttpWebRequest` sends this header by default; set `ServicePointManager.Expect100Continue` to `false` in the client if that is a problem. + +## Request Body Processing + +| Rule | Variable | Default | Purpose | +|------|----------|---------|---------| +| 900010 | `tx.enforce_bodyproc_urlencoded` | 0 | Set to 1 to force the URLENCODED body processor on requests whose content type did not activate any processor. Closes a bypass at the cost of parsing bodies that may not be form data | +| 900950 | `tx.crs_validate_utf8_encoding` | 1 | Validates that request contents are valid UTF-8 (rule 920250). Enabled by default since CRS 4.28.0. Set to 0 if your site uses a different encoding, otherwise expect false positives | + +## Argument and Upload Limits + +Enforced by rules 920360 to 920410 at paranoia level 1. + +| Rule | Variable | Default | Purpose | +|------|----------|---------|---------| +| 900300 | `tx.max_num_args` | 255 | Maximum number of request arguments | +| 900310 | `tx.arg_name_length` | 100 | Maximum length of an argument name | +| 900320 | `tx.arg_length` | 400 | Maximum length of an argument value | +| 900330 | `tx.total_arg_length` | 64000 | Maximum combined length of all arguments | +| 900340 | `tx.max_file_size` | 1048576 | Maximum size of a single uploaded file in bytes | +| 900350 | `tx.combined_file_sizes` | 1048576 | Maximum combined size of all uploaded files in bytes | + +## Other Settings + +| Rule | Variable | Default | Purpose | +|------|----------|---------|---------| +| 900130 | `tx.enable_default_collections` | 0 | Set to 1 to initialize the Global and IP collections used by plugins such as DoS protection. Core rules don't need them | +| 900400 | `tx.sampling_percentage` | 100 | Percentage of requests inspected by CRS, see [sampling mode]({{% ref "2-4-sampling_mode.md" %}}) | +| 900500 | `tx.crs_skip_response_analysis` | 0 | Set to 1 to skip all response rules (phases 3 and 4). See the response filter denial of service warning in the [installation guide]({{% ref "1-1-crs-installation.md" %}}) | +| 900990 | `tx.crs_setup_version` | release number | Set unconditionally. Rule 901001 denies all requests when it is missing | diff --git a/content/3-about-rules/creating.md b/content/3-about-rules/creating.md index 563749d0..aa3d4028 100644 --- a/content/3-about-rules/creating.md +++ b/content/3-about-rules/creating.md @@ -100,7 +100,7 @@ to remember. implied. 3. Every SecRule must have an ACTION. The only required action is id, however, several actions are implied by SecDefaultAction (default - phase:2,log,auditlog,pass) + `log,auditlog,pass` for phases 1 through 5, as set in crs-setup.conf) 4. Every SecRule must have an phase ACTION, this tells the rule when to fire. If no phase is included the default is phase:2. 5. Every SecRule must have a disruptive ACTION. This is an action that diff --git a/content/5-advanced-topics/5-1-self-contained-mode.md b/content/5-advanced-topics/5-1-self-contained-mode.md index 2a0630db..4df6463f 100644 --- a/content/5-advanced-topics/5-1-self-contained-mode.md +++ b/content/5-advanced-topics/5-1-self-contained-mode.md @@ -10,12 +10,17 @@ aliases: ["../miscellaneous/self_contained_mode"] ### Traditional Detection Mode (deprecated) -The default mode in CRS 3.x is Anomaly Scoring mode, you can verify this -is your mode by checking that the SecDefaultAction line in the -crs-setup.conf file usees the pass action: +The default mode in CRS is Anomaly Scoring mode. You can verify this +is your mode by checking that the SecDefaultAction lines in the +crs-setup.conf file use the pass action. Since CRS 4.28.0 a default is set +for every phase: ```apache -SecDefaultAction "phase:2,pass,log" +SecDefaultAction "phase:1,log,auditlog,pass" +SecDefaultAction "phase:2,log,auditlog,pass" +SecDefaultAction "phase:3,log,auditlog,pass" +SecDefaultAction "phase:4,log,auditlog,pass" +SecDefaultAction "phase:5,log,auditlog,pass" ``` {{% notice style="warning" icon="rotate-left" %}} @@ -34,18 +39,21 @@ execute any disruptive/logging actions specified on the current rule. ### Configuring Traditional Mode If you want to run the CRS in Traditional mode, you can do this easily -by modifying the SecDefaultAction directive in the crs-setup.conf file -to use a disruptive action other than the default \'pass\', such as -deny: +by modifying the SecDefaultAction directives in the crs-setup.conf file +to use a disruptive action other than the default `pass`, such as +deny. Change the request phases (1 and 2) together; the response phases +can stay on `pass` or be changed in the same way: ```apache # Default (Anomaly Mode) -SecDefaultAction "phase:2,pass,log" +SecDefaultAction "phase:1,log,auditlog,pass" +SecDefaultAction "phase:2,log,auditlog,pass" ``` ```apache # Updated To Enable Traditional Mode -SecDefaultAction "phase:2,deny,status:403,log" +SecDefaultAction "phase:1,log,auditlog,deny,status:403" +SecDefaultAction "phase:2,log,auditlog,deny,status:403" ``` ### Pros and Cons of Traditional Detection Mode From 732235d4ea1e0ba6bcc0daa5c8c3c2b5595e0fcc Mon Sep 17 00:00:00 2001 From: Felipe Zipitria Date: Wed, 14 Oct 2026 07:48:09 -0300 Subject: [PATCH 2/3] ci: exclude debuggex.com from link checker The site currently returns 502 Bad Gateway, which fails the PR link check for an unrelated page. Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_016dPLzCS17k6fiP7qf35iZi --- lychee.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lychee.toml b/lychee.toml index fbfdfc8a..99f12827 100644 --- a/lychee.toml +++ b/lychee.toml @@ -1,4 +1,4 @@ -exclude = ["github.com/coreruleset.*/SECURITY.md", "github.com/coreruleset.*/.github/.*", "github.com/coreruleset/coreruleset/blob/?$", "https://www.akamai.com/products/app-and-api-protector", "https://www.drupal.org", "https://www.dokuwiki.org"] +exclude = ["github.com/coreruleset.*/SECURITY.md", "github.com/coreruleset.*/.github/.*", "github.com/coreruleset/coreruleset/blob/?$", "https://www.akamai.com/products/app-and-api-protector", "https://www.drupal.org", "https://www.dokuwiki.org", "https://www.debuggex.com"] user_agent = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:132.0) Gecko/20100101 Firefox/132.0" no_progress = true cache = true From a4a586db9ff3bf63f57961e43889518905bc6702 Mon Sep 17 00:00:00 2001 From: Felipe Zipitria Date: Wed, 14 Oct 2026 09:08:15 -0300 Subject: [PATCH 3/3] docs: name the fields validated by rule 920250 Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_016dPLzCS17k6fiP7qf35iZi --- content/2-how-crs-works/2-5-configuration-settings.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/2-how-crs-works/2-5-configuration-settings.md b/content/2-how-crs-works/2-5-configuration-settings.md index 42b294e2..ffe213d8 100644 --- a/content/2-how-crs-works/2-5-configuration-settings.md +++ b/content/2-how-crs-works/2-5-configuration-settings.md @@ -98,7 +98,7 @@ The default basic list blocks headers that let a client alter how the request is | Rule | Variable | Default | Purpose | |------|----------|---------|---------| | 900010 | `tx.enforce_bodyproc_urlencoded` | 0 | Set to 1 to force the URLENCODED body processor on requests whose content type did not activate any processor. Closes a bypass at the cost of parsing bodies that may not be form data | -| 900950 | `tx.crs_validate_utf8_encoding` | 1 | Validates that request contents are valid UTF-8 (rule 920250). Enabled by default since CRS 4.28.0. Set to 0 if your site uses a different encoding, otherwise expect false positives | +| 900950 | `tx.crs_validate_utf8_encoding` | 1 | Validates that `REQUEST_FILENAME`, `ARGS`, and `ARGS_NAMES` are valid UTF-8 (rule 920250). Enabled by default since CRS 4.28.0. Set to 0 if your site uses a different encoding, otherwise expect false positives | ## Argument and Upload Limits