diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 5cf07ad..e3b5a4e 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -76,6 +76,13 @@ jobs: ENVOY_IMAGE=$image go run mage.go e2e done + - name: Spins up the example + run: go run mage.go runExample + + - name: Run example tests + shell: bash + run: ./example/readme-tests.sh + - name: Set up Docker Buildx uses: docker/setup-buildx-action@v2 diff --git a/README.md b/README.md index dd41870..985c0eb 100644 --- a/README.md +++ b/README.md @@ -104,10 +104,28 @@ go run mage.go ftw ``` Take a look at its config file [ftw.yml](./ftw/ftw.yml) for details about tests currently excluded. -### Spinning up the coraza-proxy-wasm for manual tests - -Via the commands `setup` and `teardown` you can spin up and tear down the test environment. Envoy with the coraza-wasm filter will be reachable at `localhost:8080`. -In order to monitor envoy logs while performing requests run: -``` -docker-compose -f ./ftw/docker-compose.yml logs -f envoy-logs +## Example: Spinning up the coraza-wasm-filter for manual tests +Once the filter is built, via the commands `RunExample` and `teardownExample` you can spin up and tear down the test environment. Envoy with the coraza-wasm filter will be reachable at `localhost:8080`. The filter is configured with the CRS loaded working in Anomaly Scoring mode. For details and locally tweaking the configuration refer to [coraza-demo.conf](./rules/coraza-demo.conf) and [crs-setup-demo.conf](./rules/crs-setup-demo.conf). +In order to monitor envoy logs while performing requests you can run: +- Envoy logs: `docker-compose -f ./example/docker-compose.yml logs -f envoy-logs`. +- Critical wasm (audit) logs: `docker-compose -f ./example/docker-compose.yml logs -f wasm-logs` + +### Manual requests +Run `./example/readme-tests.sh` in order to run the following requests against the just set up test environment, otherwise manually execute them on your own: +```bash +# True positive requests: +# XSS phase 1 +curl -I 'http://localhost:8080/anything?arg=' +# SQLI phase 2 (reading the body request) +curl -i -X POST 'http://localhost:8080/anything' --data "1%27%20ORDER%20BY%203--%2B" +# Triggers a CRS scanner detection rule (913100) +curl -I --user-agent "Grabber/0.1 (X11; U; Linux i686; en-US; rv:1.7)" -H "Host: localhost" -H "Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5" localhost:8080 + +# True negative requests: +# A GET request with a harmless argument +curl -I 'http://localhost:8080/anything?arg=arg_1' +# An usual user-agent +curl -I --user-agent "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36" localhost:8080 +# A payload (reading the body request) +curl -i -X POST 'http://localhost:8080/anything' --data "this is a payload" ``` diff --git a/e2e/tests.sh b/e2e/tests.sh old mode 100755 new mode 100644 index e1421bd..7dbc7e6 --- a/e2e/tests.sh +++ b/e2e/tests.sh @@ -48,4 +48,4 @@ if [[ "$status_code" -ne 403 ]] ; then fi echo "[Ok] Got status code $status_code, expected 403" -echo "[Done] All tests passed" \ No newline at end of file +echo "[Done] All tests passed" diff --git a/example/docker-compose.yml b/example/docker-compose.yml new file mode 100644 index 0000000..cab8056 --- /dev/null +++ b/example/docker-compose.yml @@ -0,0 +1,55 @@ +services: + httpbin: + image: kennethreitz/httpbin:latest + chown: + image: alpine:3.16 + command: + - /bin/sh + - -c + - chown -R 101:101 /home/envoy/logs + volumes: + - logs:/home/envoy/logs:rw + envoy: + depends_on: + - chown + - httpbin + image: envoyproxy/envoy:v1.23-latest + command: + - -c + - /conf/envoy-config.yaml + - --log-level + - info + - --component-log-level + - wasm:debug + - --log-format [%Y-%m-%d %T.%f][%t][%l][%n] [%g:%#] %v + - --log-path + - /home/envoy/logs/envoy.log + volumes: + - ../build:/build + - .:/conf + - logs:/home/envoy/logs:rw + ports: + - 8080:80 + wasm-logs: + depends_on: + - envoy + image: debian:11-slim + entrypoint: bash + command: + - -c + - tail -c +0 -f /home/envoy/logs/envoy.log | grep --line-buffered "[critical][wasm]" | tee /home/envoy/logs/ftw.log + volumes: + - logs:/home/envoy/logs:rw + envoy-logs: + depends_on: + - envoy + - wasm-logs + image: debian:11-slim + entrypoint: bash + command: + - -c + - tail -c +0 -f /home/envoy/logs/envoy.log + volumes: + - logs:/home/envoy/logs:ro +volumes: + logs: diff --git a/example/envoy-config.yaml b/example/envoy-config.yaml new file mode 100644 index 0000000..f4478e9 --- /dev/null +++ b/example/envoy-config.yaml @@ -0,0 +1,68 @@ +static_resources: + listeners: + - address: + socket_address: + address: 0.0.0.0 + port_value: 80 + filter_chains: + - filters: + - name: envoy.filters.network.http_connection_manager + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + stat_prefix: ingress_http + codec_type: auto + http_protocol_options: + accept_http_10: true + route_config: + virtual_hosts: + - name: local_route + domains: + - "*" + routes: + - match: + prefix: "/" + route: + cluster: local_server + http_filters: + - name: envoy.filters.http.wasm + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm + config: + name: "coraza-filter" + root_id: "" + configuration: + "@type": "type.googleapis.com/google.protobuf.StringValue" + value: | + { + "rules": [ + {"inline": "Include coraza-demo.conf"}, + {"inline": "Include crs-setup-demo.conf"}, + {"inline": "SecDebugLogLevel 3"}, + {"inline": "Include crs/*.conf"}, + {"inline": "SecRule ARGS:id \"@eq 0\" \"id:1, phase:1,deny, status:403,msg:'Invalid id',log,auditlog\""} + ] + } + vm_config: + runtime: "envoy.wasm.runtime.v8" + vm_id: "my_vm_id" + code: + local: + filename: "build/main.wasm" + - name: envoy.filters.http.router + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + + clusters: + - name: local_server + connect_timeout: 6000s + type: strict_dns + lb_policy: round_robin + load_assignment: + cluster_name: local_server + endpoints: + - lb_endpoints: + - endpoint: + address: + socket_address: + address: httpbin + port_value: 80 diff --git a/example/readme-tests.sh b/example/readme-tests.sh new file mode 100755 index 0000000..ac3be67 --- /dev/null +++ b/example/readme-tests.sh @@ -0,0 +1,110 @@ +#!/bin/bash +# Copyright 2022 The OWASP Coraza contributors +# SPDX-License-Identifier: Apache-2.0 +ENVOY_HOST=${ENVOY_HOST:-"localhost:8080"} + +[[ "${DEBUG}" == "true" ]] && set -x + +envoy_url_echo="http://${ENVOY_HOST}/anything" + +okayBodyPayload="hello" +maliciousBodyPayload="maliciouspayload" +bodyPayloadForResponseBodyTrueNegative="Hello world" +bodyPayloadForResponseBody="responsebodycode" + +# wait_for_service waits until the given URL returns a 200 status code. +# $1: The URL to send requests to. +# $2: The max number of requests to send before giving up. +function wait_for_service() { + local status_code="000" + local url=${1} + local max=${2} + while [[ "${status_code}" -ne "200" ]]; do + status_code=$(curl --write-out "%{http_code}" --silent --output /dev/null "${url}") + sleep 1 + echo -ne "[Wait] Waiting for response from ${url}. Timeout: ${max}s \r" + ((max-=1)) + if [[ "${max}" -eq 0 ]]; then + echo "[Fail] Timeout waiting for response from ${url}, make sure the server is running." + exit 1 + fi + done + echo -e "\n[Ok] Got status code ${status_code}" +} + +# check_status sends HTTP requests to the given URL and expects a given response code. +# $1: The URL to send requests to. +# $2: The expected status code. +# $3-N: The rest of the arguments will be passed to the curl command as additional arguments +# to customize the HTTP call. +function check_status() { + local url=${1} + local status=${2} + local args=("${@:3}" --write-out '%{http_code}' --silent --output /dev/null) + status_code=$(curl "${args[@]}" "${url}") + if [[ "${status_code}" -ne ${status} ]] ; then + echo "[Fail] Unexpected response with code ${status_code} from ${url}" + exit 1 + fi + echo "[Ok] Got status code ${status_code}, expected ${status}" +} + +# check_body sends the given HTTP request and checks the response body. +# $1: The URL to send requests to. +# $2: true/false indicating if an empty body is expected or not. +# $3-N: The rest of the arguments will be passed to the curl command as additional arguments +# to customize the HTTP call. +function check_body() { + local url=${1} + local empty=${2} + local args=("${@:3}" --silent) + response_body=$(curl "${args[@]}" "${url}") + if [[ "${empty}" == "true" ]] && [[ -n "${response_body}" ]]; then + echo -e "[Fail] Unexpected response with a body. Body dump:\n${response_body}" + exit 1 + fi + if [[ "${empty}" != "true" ]] && [[ -z "${response_body}" ]]; then + echo -e "[Fail] Unexpected response with a body. Body dump:\n${response_body}" + exit 1 + fi + echo "[Ok] Got response with an expected body (empty=${empty})" +} + +step=1 +total_steps=7 + +# Testing if the server is up +echo "[${step}/${total_steps}] Testing application reachability" +wait_for_service "${envoy_url_echo}" 20 + +# Testing XSS phase 1 +((step+=1)) +echo "[${step}/${total_steps}] Testing XSS at request headers" +check_status "${envoy_url_echo}?arg=" 403 + +# Testing SQLI phase 2 +((step+=1)) +echo "[${step}/${total_steps}] Testing SQLi at request body" +check_status "${envoy_url_echo}" 403 -X POST --data "1%27%20ORDER%20BY%203--%2B" + +# Triggers a CRS scanner detection rule (913100) +((step+=1)) +echo "[${step}/${total_steps}] (onRequestBody) Testing CRS rule 913100" +check_status "${envoy_url_echo}" 403 --user-agent "Grabber/0.1 (X11; U; Linux i686; en-US; rv:1.7)" -H "Host: localhost" -H "Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5" + +# True negative GET request +((step+=1)) +echo "[${step}/${total_steps}] True negative GET request" +check_status "${envoy_url_echo}?arg=arg_1" 200 + +# True negative GET request with an usual user-agent +((step+=1)) +echo "[${step}/${total_steps}] True negative GET request with user-agent" +check_status "${envoy_url_echo}" 200 --user-agent "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36" + +# True negative POST request with a payload +((step+=1)) +echo "[${step}/${total_steps}] True negative POST request" +check_status "${envoy_url_echo}" 200 --data "this is a payload" + +echo "[Done] All examples request worked as expected" diff --git a/ftw/tests.sh b/ftw/tests.sh index 244aa69..7af5f99 100755 --- a/ftw/tests.sh +++ b/ftw/tests.sh @@ -8,7 +8,7 @@ cd /workspace step=1 total_steps=3 -max_retries=10 #seconds for the server reachability timeout +max_retries=15 #seconds for the server reachability timeout host=${1:-envoy} health_url="http://${host}:80" unfiltered_url="http://${host}:80/home" @@ -21,7 +21,7 @@ while [[ "$status_code" -eq "000" ]]; do status_code=$(curl --write-out "%{http_code}" --silent --output /dev/null $health_url) sleep 1 echo -ne "[Wait] Waiting for response from $health_url. Timeout: ${max_retries}s \r" - ((max_retries-=1)) + let "max_retries--" if [[ "$max_retries" -eq 0 ]] ; then echo "[Fail] Timeout waiting for response from $health_url, make sure the server is running." exit 1 diff --git a/magefile.go b/magefile.go index 49977d2..fc7387a 100644 --- a/magefile.go +++ b/magefile.go @@ -63,7 +63,7 @@ func Lint() error { return nil } -// Test runs all tests. +// Test runs all unit tests. func Test() error { return sh.RunV("go", "test", "./...") } @@ -172,4 +172,14 @@ func Ftw() error { return sh.RunWithV(env, "docker-compose", "--file", "ftw/docker-compose.yml", "run", "--rm", "ftw") } +// RunExample spins up the test environment, access at http://localhost:8080. Requires docker-compose. +func RunExample() error { + return sh.RunV("docker-compose", "--file", "example/docker-compose.yml", "up", "-d", "envoy-logs") +} + +// TeardownExample tears down the test environment. Requires docker-compose. +func TeardownExample() error { + return sh.RunV("docker-compose", "--file", "example/docker-compose.yml", "down") +} + var Default = Build diff --git a/rules/coraza-demo.conf b/rules/coraza-demo.conf new file mode 100644 index 0000000..7e2f89b --- /dev/null +++ b/rules/coraza-demo.conf @@ -0,0 +1,250 @@ +# -- Rule engine initialization ---------------------------------------------- + +# Enable Coraza, attaching it to every transaction. Use detection +# only to start with, because that minimises the chances of post-installation +# disruption. +# +SecRuleEngine On + + +# -- Request body handling --------------------------------------------------- + +# Allow Coraza to access request bodies. If you don't, Coraza +# won't be able to see any POST parameters, which opens a large security +# hole for attackers to exploit. +# +SecRequestBodyAccess On + +# Enable XML request body parser. +# Initiate XML Processor in case of xml content-type +# +SecRule REQUEST_HEADERS:Content-Type "(?:application(?:/soap\+|/)|text/)xml" \ + "id:'200000',phase:1,t:none,t:lowercase,pass,nolog,ctl:requestBodyProcessor=XML" + +# Enable JSON request body parser. +# Initiate JSON Processor in case of JSON content-type; change accordingly +# if your application does not use 'application/json' +# +SecRule REQUEST_HEADERS:Content-Type "application/json" \ + "id:'200001',phase:1,t:none,t:lowercase,pass,nolog,ctl:requestBodyProcessor=JSON" + +# Sample rule to enable JSON request body parser for more subtypes. +# Uncomment or adapt this rule if you want to engage the JSON +# Processor for "+json" subtypes +# +#SecRule REQUEST_HEADERS:Content-Type "^application/.+[+]json$" \ +# "id:'200006',phase:1,t:none,t:lowercase,pass,nolog,ctl:requestBodyProcessor=JSON" + +# Maximum request body size we will accept for buffering. If you support +# file uploads then the value given on the first line has to be as large +# as the largest file you are willing to accept. The second value refers +# to the size of data, with files excluded. You want to keep that value as +# low as practical. +# +SecRequestBodyLimit 13107200 +SecRequestBodyNoFilesLimit 131072 + +# What to do if the request body size is above our configured limit. +# Keep in mind that this setting will automatically be set to ProcessPartial +# when SecRuleEngine is set to DetectionOnly mode in order to minimize +# disruptions when initially deploying Coraza. +# +SecRequestBodyLimitAction Reject + +# Verify that we've correctly processed the request body. +# As a rule of thumb, when failing to process a request body +# you should reject the request (when deployed in blocking mode) +# or log a high-severity alert (when deployed in detection-only mode). +# +SecRule REQBODY_ERROR "!@eq 0" \ +"id:'200002', phase:2,t:none,log,deny,status:400,msg:'Failed to parse request body.',logdata:'%{reqbody_error_msg}',severity:2" + +# By default be strict with what we accept in the multipart/form-data +# request body. If the rule below proves to be too strict for your +# environment consider changing it to detection-only. You are encouraged +# _not_ to remove it altogether. +# +SecRule MULTIPART_STRICT_ERROR "!@eq 0" \ +"id:'200003',phase:2,t:none,log,deny,status:400, \ +msg:'Multipart request body failed strict validation: \ +PE %{REQBODY_PROCESSOR_ERROR}, \ +BQ %{MULTIPART_BOUNDARY_QUOTED}, \ +BW %{MULTIPART_BOUNDARY_WHITESPACE}, \ +DB %{MULTIPART_DATA_BEFORE}, \ +DA %{MULTIPART_DATA_AFTER}, \ +HF %{MULTIPART_HEADER_FOLDING}, \ +LF %{MULTIPART_LF_LINE}, \ +SM %{MULTIPART_MISSING_SEMICOLON}, \ +IQ %{MULTIPART_INVALID_QUOTING}, \ +IP %{MULTIPART_INVALID_PART}, \ +IH %{MULTIPART_INVALID_HEADER_FOLDING}, \ +FL %{MULTIPART_FILE_LIMIT_EXCEEDED}'" + +# Did we see anything that might be a boundary? +# +# Here is a short description about the Coraza Multipart parser: the +# parser returns with value 0, if all "boundary-like" line matches with +# the boundary string which given in MIME header. In any other cases it returns +# with different value, eg. 1 or 2. +# +# The RFC 1341 descript the multipart content-type and its syntax must contains +# only three mandatory lines (above the content): +# * Content-Type: multipart/mixed; boundary=BOUNDARY_STRING +# * --BOUNDARY_STRING +# * --BOUNDARY_STRING-- +# +# First line indicates, that this is a multipart content, second shows that +# here starts a part of the multipart content, third shows the end of content. +# +# If there are any other lines, which starts with "--", then it should be +# another boundary id - or not. +# +# After 3.0.3, there are two kinds of types of boundary errors: strict and permissive. +# +# If multipart content contains the three necessary lines with correct order, but +# there are one or more lines with "--", then parser returns with value 2 (non-zero). +# +# If some of the necessary lines (usually the start or end) misses, or the order +# is wrong, then parser returns with value 1 (also a non-zero). +# +# You can choose, which one is what you need. The example below contains the +# 'strict' mode, which means if there are any lines with start of "--", then +# Coraza blocked the content. But the next, commented example contains +# the 'permissive' mode, then you check only if the necessary lines exists in +# correct order. Whit this, you can enable to upload PEM files (eg "----BEGIN.."), +# or other text files, which contains eg. HTTP headers. +# +# The difference is only the operator - in strict mode (first) the content blocked +# in case of any non-zero value. In permissive mode (second, commented) the +# content blocked only if the value is explicit 1. If it 0 or 2, the content will +# allowed. +# + +# +# See #1747 and #1924 for further information on the possible values for +# MULTIPART_UNMATCHED_BOUNDARY. +# +SecRule MULTIPART_UNMATCHED_BOUNDARY "@eq 1" \ + "id:'200004',phase:2,t:none,log,deny,msg:'Multipart parser detected a possible unmatched boundary.'" + +# Some internal errors will set flags in TX and we will need to look for these. +# All of these are prefixed with "MSC_". The following flags currently exist: +# +# COR_PCRE_LIMITS_EXCEEDED: PCRE match limits were exceeded. +# +SecRule TX:/^COR_/ "!@streq 0" \ + "id:'200005',phase:2,t:none,deny,msg:'Coraza internal error flagged: %{MATCHED_VAR_NAME}'" + + +# -- Response body handling -------------------------------------------------- + +# Allow Coraza to access response bodies. +# You should have this directive enabled in order to identify errors +# and data leakage issues. +# +# Do keep in mind that enabling this directive does increases both +# memory consumption and response latency. +# +SecResponseBodyAccess On + +# Which response MIME types do you want to inspect? You should adjust the +# configuration below to catch documents but avoid static files +# (e.g., images and archives). +# +SecResponseBodyMimeType text/plain text/html text/xml + +# Buffer response bodies of up to 512 KB in length. +SecResponseBodyLimit 524288 + +# What happens when we encounter a response body larger than the configured +# limit? By default, we process what we have and let the rest through. +# That's somewhat less secure, but does not break any legitimate pages. +# +SecResponseBodyLimitAction ProcessPartial + + +# -- Filesystem configuration ------------------------------------------------ + +# The location where Coraza stores temporary files (for example, when +# it needs to handle a file upload that is larger than the configured limit). +# +# This default setting is chosen due to all systems have /tmp available however, +# this is less than ideal. It is recommended that you specify a location that's private. +# +SecTmpDir /tmp/ + +# The location where Coraza will keep its persistent data. This default setting +# is chosen due to all systems have /tmp available however, it +# too should be updated to a place that other users can't access. +# +SecDataDir /tmp/ + + +# -- File uploads handling configuration ------------------------------------- + +# The location where Coraza stores intercepted uploaded files. This +# location must be private to Coraza. You don't want other users on +# the server to access the files, do you? +# +#SecUploadDir /opt/coraza/var/upload/ + +# By default, only keep the files that were determined to be unusual +# in some way (by an external inspection script). For this to work you +# will also need at least one file inspection rule. +# +#SecUploadKeepFiles RelevantOnly + +# Uploaded files are by default created with permissions that do not allow +# any other user to access them. You may need to relax that if you want to +# interface Coraza to an external program (e.g., an anti-virus). +# +#SecUploadFileMode 0600 + + +# -- Debug log configuration ------------------------------------------------- + +# Default debug log path +# Debug levels: +# 1: fatal +# 2: panic +# 3: error +# 4: warn +# 5: info +# 6: debug +# Most logging has not been implemented because it will be replaced with +# advanced rule profiling options +#SecDebugLog /opt/coraza/var/log/debug.log +SecDebugLogLevel 3 + + +# -- Audit log configuration ------------------------------------------------- + +# Log the transactions that are marked by a rule, as well as those that +# trigger a server error (determined by a 5xx or 4xx, excluding 404, +# level response status codes). +# +SecAuditEngine On +SecAuditLogRelevantStatus "^(?:(5|4)(0|1)[0-9])$" + +# Log everything we know about a transaction. +SecAuditLogParts ABIJDEFHZ + +# Use a single file for logging. This is much easier to look at, but +# assumes that you will use the audit log only occasionally. +# +SecAuditLogType Serial + + +# -- Miscellaneous ----------------------------------------------------------- + +# Use the most commonly used application/x-www-form-urlencoded parameter +# separator. There's probably only one application somewhere that uses +# something else so don't expect to change this value. +# +SecArgumentSeparator & + +# Settle on version 0 (zero) cookies, as that is what most applications +# use. Using an incorrect cookie version may open your installation to +# evasion attacks (against the rules that examine named cookies). +# +SecCookieFormat 0 diff --git a/rules/crs-setup-demo.conf b/rules/crs-setup-demo.conf new file mode 100644 index 0000000..479cd7d --- /dev/null +++ b/rules/crs-setup-demo.conf @@ -0,0 +1,689 @@ +# ------------------------------------------------------------------------ +# OWASP ModSecurity Core Rule Set ver.4.0.0-rc1 +# Copyright (c) 2006-2020 Trustwave and contributors. All rights reserved. +# Copyright (c) 2021-2022 Core Rule Set project. All rights reserved. +# +# The OWASP ModSecurity Core Rule Set is distributed under +# Apache Software License (ASL) version 2 +# Please see the enclosed LICENSE file for full details. +# ------------------------------------------------------------------------ + + +# +# -- [[ Introduction ]] -------------------------------------------------------- +# +# The OWASP ModSecurity Core Rule Set (CRS) is a set of generic attack +# detection rules that provide a base level of protection for any web +# application. They are written for the open source, cross-platform +# ModSecurity Web Application Firewall. +# +# See also: +# https://coreruleset.org/ +# https://github.com/coreruleset/coreruleset +# https://owasp.org/www-project-modsecurity-core-rule-set/ +# + + +# +# -- [[ System Requirements ]] ------------------------------------------------- +# +# CRS requires ModSecurity version 2.8.0 or above. +# We recommend to always use the newest ModSecurity version. +# +# The configuration directives/settings in this file are used to control +# the OWASP ModSecurity CRS. These settings do **NOT** configure the main +# ModSecurity settings (modsecurity.conf) such as SecRuleEngine, +# SecRequestBodyAccess, SecAuditEngine, SecDebugLog, and XML processing. +# +# The CRS assumes that modsecurity.conf has been loaded. It is bundled with +# ModSecurity. If you don't have it, you can get it from: +# 2.x: https://raw.githubusercontent.com/SpiderLabs/ModSecurity/v2/master/modsecurity.conf-recommended +# 3.x: https://raw.githubusercontent.com/SpiderLabs/ModSecurity/v3/master/modsecurity.conf-recommended +# +# The order of file inclusion in your webserver configuration should always be: +# 1. modsecurity.conf +# 2. crs-setup.conf (this file) +# 3. rules/*.conf (the CRS rule files) +# +# Please refer to the INSTALL file for detailed installation instructions. +# + + +# +# -- [[ Mode of Operation: Anomaly Scoring vs. Self-Contained ]] --------------- +# +# The CRS can run in two modes: +# +# -- [[ Anomaly Scoring Mode (default) ]] -- +# In CRS3, anomaly mode is the default and recommended mode, since it gives the +# most accurate log information and offers the most flexibility in setting your +# blocking policies. It is also called "collaborative detection mode". +# In this mode, each matching rule increases an 'anomaly score'. +# At the conclusion of the inbound rules, and again at the conclusion of the +# outbound rules, the anomaly score is checked, and the blocking evaluation +# rules apply a disruptive action, by default returning an error 403. +# +# -- [[ Self-Contained Mode ]] -- +# In this mode, rules apply an action instantly. This was the CRS2 default. +# It can lower resource usage, at the cost of less flexibility in blocking policy +# and less informative audit logs (only the first detected threat is logged). +# Rules inherit the disruptive action that you specify (i.e. deny, drop, etc). +# The first rule that matches will execute this action. In most cases this will +# cause evaluation to stop after the first rule has matched, similar to how many +# IDSs function. +# +# -- [[ Alert Logging Control ]] -- +# In the mode configuration, you must also adjust the desired logging options. +# There are three common options for dealing with logging. By default CRS enables +# logging to the webserver error log (or Event viewer) plus detailed logging to +# the ModSecurity audit log (configured under SecAuditLog in modsecurity.conf). +# +# - To log to both error log and ModSecurity audit log file, use: "log,auditlog" +# - To log *only* to the ModSecurity audit log file, use: "nolog,auditlog" +# - To log *only* to the error log file, use: "log,noauditlog" +# +# Examples for the various modes follow. +# You must leave one of the following options enabled. +# Note that you must specify the same line for phase:1 and phase:2. +# + +# Default: Anomaly Scoring mode, log to error log, log to ModSecurity audit log +# - By default, offending requests are blocked with an error 403 response. +# - To change the disruptive action, see RESPONSE-999-EXCLUSION-RULES-AFTER-CRS.conf.example +# and review section 'Changing the Disruptive Action for Anomaly Mode'. +# - In Apache, you can use ErrorDocument to show a friendly error page or +# perform a redirect: https://httpd.apache.org/docs/2.4/custom-error.html +# +SecDefaultAction "phase:1,log,auditlog,pass" +SecDefaultAction "phase:2,log,auditlog,pass" + +# Example: Anomaly Scoring mode, log only to ModSecurity audit log +# - By default, offending requests are blocked with an error 403 response. +# - To change the disruptive action, see RESPONSE-999-EXCLUSION-RULES-AFTER-CRS.conf.example +# and review section 'Changing the Disruptive Action for Anomaly Mode'. +# - In Apache, you can use ErrorDocument to show a friendly error page or +# perform a redirect: https://httpd.apache.org/docs/2.4/custom-error.html +# +# SecDefaultAction "phase:1,nolog,auditlog,pass" +# SecDefaultAction "phase:2,nolog,auditlog,pass" + +# Example: Self-contained mode, return error 403 on blocking +# - In this configuration the default disruptive action becomes 'deny'. After a +# rule triggers, it will stop processing the request and return an error 403. +# - You can also use a different error status, such as 404, 406, et cetera. +# - In Apache, you can use ErrorDocument to show a friendly error page or +# perform a redirect: https://httpd.apache.org/docs/2.4/custom-error.html +# +# SecDefaultAction "phase:1,log,auditlog,deny,status:403" +# SecDefaultAction "phase:2,log,auditlog,deny,status:403" + +# Example: Self-contained mode, redirect back to homepage on blocking +# - In this configuration the 'tag' action includes the Host header data in the +# log. This helps to identify which virtual host triggered the rule (if any). +# - Note that this might cause redirect loops in some situations; for example +# if a Cookie or User-Agent header is blocked, it will also be blocked when +# the client subsequently tries to access the homepage. You can also redirect +# to another custom URL. +# SecDefaultAction "phase:1,log,auditlog,redirect:'http://%{request_headers.host}/',tag:'Host: %{request_headers.host}'" +# SecDefaultAction "phase:2,log,auditlog,redirect:'http://%{request_headers.host}/',tag:'Host: %{request_headers.host}'" + + +# +# -- [[ Paranoia Level Initialization ]] --------------------------------------- +# +# The Paranoia Level (PL) setting allows you to choose the desired level +# of rule checks that will add to your anomaly scores. +# +# With each paranoia level increase, the CRS enables additional rules +# giving you a higher level of security. However, higher paranoia levels +# also increase the possibility of blocking some legitimate traffic due to +# false alarms (also named false positives or FPs). If you use higher +# paranoia levels, it is likely that you will need to add some exclusion +# rules for certain requests and applications receiving complex input. +# +# - A paranoia level of 1 is default. In this level, most core rules +# are enabled. PL1 is advised for beginners, installations +# covering many different sites and applications, and for setups +# with standard security requirements. +# At PL1 you should face FPs rarely. If you encounter FPs, please +# open an issue on the CRS GitHub site and don't forget to attach your +# complete Audit Log record for the request with the issue. +# - Paranoia level 2 includes many extra rules, for instance enabling +# many regexp-based SQL and XSS injection protections, and adding +# extra keywords checked for code injections. PL2 is advised +# for moderate to experienced users desiring more complete coverage +# and for installations with elevated security requirements. +# PL2 comes with some FPs which you need to handle. +# - Paranoia level 3 enables more rules and keyword lists, and tweaks +# limits on special characters used. PL3 is aimed at users experienced +# at the handling of FPs and at installations with a high security +# requirement. +# - Paranoia level 4 further restricts special characters. +# The highest level is advised for experienced users protecting +# installations with very high security requirements. Running PL4 will +# likely produce a very high number of FPs which have to be +# treated before the site can go productive. +# +# All rules will log their PL to the audit log; +# example: [tag "paranoia-level/2"]. This allows you to deduct from the +# audit log how the WAF behavior is affected by paranoia level. +# +# It is important to also look into the variable +# tx.enforce_bodyproc_urlencoded (Enforce Body Processor URLENCODED) +# defined below. Enabling it closes a possible bypass of CRS. +# +# Uncomment this rule to change the default: +# +# SecAction \ +# "id:900000,\ +# phase:1,\ +# nolog,\ +# pass,\ +# t:none,\ +# setvar:tx.blocking_paranoia_level=1" + + +# It is possible to execute rules from a higher paranoia level but not include +# them in the anomaly scoring. This allows you to take a well-tuned system on +# paranoia level 1 and add rules from paranoia level 2 without having to fear +# the new rules would lead to false positives that raise your score above the +# threshold. +# This optional feature is enabled by uncommenting the following rule and +# setting the tx.detection_paranoia_level. +# Technically, rules up to the level defined in tx.detection_paranoia_level +# will be executed, but only the rules up to tx.blocking_paranoia_level affect the +# anomaly scores. +# By default, tx.detection_paranoia_level is set to tx.blocking_paranoia_level. +# tx.detection_paranoia_level must not be lower than tx.blocking_paranoia_level. +# +# Please notice that setting tx.detection_paranoia_level to a higher paranoia +# level results in a performance impact that is equally high as setting +# tx.blocking_paranoia_level to said level. +# +# SecAction \ +# "id:900001,\ +# phase:1,\ +# nolog,\ +# pass,\ +# t:none,\ +# setvar:tx.detection_paranoia_level=1" + + +# +# -- [[ Enforce Body Processor URLENCODED ]] ----------------------------------- +# +# ModSecurity selects the body processor based on the Content-Type request +# header. But clients are not always setting the Content-Type header for their +# request body payloads. This will leave ModSecurity with limited vision into +# the payload. The variable tx.enforce_bodyproc_urlencoded lets you force the +# URLENCODED body processor in these situations. This is off by default, as it +# implies a change of the behaviour of ModSecurity beyond CRS (the body +# processor applies to all rules, not only CRS) and because it may lead to +# false positives already on paranoia level 1. However, enabling this variable +# closes a possible bypass of CRS so it should be considered. +# +# Uncomment this rule to change the default: +# +#SecAction \ +# "id:900010,\ +# phase:1,\ +# nolog,\ +# pass,\ +# t:none,\ +# setvar:tx.enforce_bodyproc_urlencoded=1" + + +# +# -- [[ Anomaly Scoring Mode Severity Levels ]] -------------------------------- +# +# Each rule in the CRS has an associated severity level. +# These are the default scoring points for each severity level. +# These settings will be used to increment the anomaly score if a rule matches. +# You may adjust these points to your liking, but this is usually not needed. +# +# - CRITICAL severity: Anomaly Score of 5. +# Mostly generated by the application attack rules (93x and 94x files). +# - ERROR severity: Anomaly Score of 4. +# Generated mostly from outbound leakage rules (95x files). +# - WARNING severity: Anomaly Score of 3. +# Generated mostly by malicious client rules (91x files). +# - NOTICE severity: Anomaly Score of 2. +# Generated mostly by the protocol rules (92x files). +# +# In anomaly mode, these scores are cumulative. +# So it's possible for a request to hit multiple rules. +# +# (Note: In this file, we use 'phase:1' to set CRS configuration variables. +# In general, 'phase:request' is used. However, we want to make absolutely sure +# that all configuration variables are set before the CRS rules are processed.) +# +#SecAction \ +# "id:900100,\ +# phase:1,\ +# nolog,\ +# pass,\ +# t:none,\ +# setvar:tx.critical_anomaly_score=5,\ +# setvar:tx.error_anomaly_score=4,\ +# setvar:tx.warning_anomaly_score=3,\ +# setvar:tx.notice_anomaly_score=2" + + +# +# -- [[ Anomaly Scoring Mode Blocking Threshold Levels ]] ---------------------- +# +# Here, you can specify at which cumulative anomaly score an inbound request, +# or outbound response, gets blocked. +# +# Most detected inbound threats will give a critical score of 5. +# Smaller violations, like violations of protocol/standards, carry lower scores. +# +# [ At default value ] +# If you keep the blocking thresholds at the defaults, the CRS will work +# similarly to previous CRS versions: a single critical rule match will cause +# the request to be blocked and logged. +# +# [ Using higher values ] +# If you want to make the CRS less sensitive, you can increase the blocking +# thresholds, for instance to 7 (which would require multiple rule matches +# before blocking) or 10 (which would require at least two critical alerts - or +# a combination of many lesser alerts), or even higher. However, increasing the +# thresholds might cause some attacks to bypass the CRS rules or your policies. +# +# [ New deployment strategy: Starting high and decreasing ] +# It is a common practice to start a fresh CRS installation with elevated +# anomaly scoring thresholds (>100) and then lower the limits as your +# confidence in the setup grows. You may also look into the Sampling +# Percentage section below for a different strategy to ease into a new +# CRS installation. +# +# [ Anomaly Threshold / Paranoia Level Quadrant ] +# +# High Anomaly Limit | High Anomaly Limit +# Low Paranoia Level | High Paranoia Level +# -> Fresh Site | -> Experimental Site +# ------------------------------------------------------ +# Low Anomaly Limit | Low Anomaly Limit +# Low Paranoia Level | High Paranoia Level +# -> Standard Site | -> High Security Site +# +# Uncomment this rule to change the defaults: +# +#SecAction \ +# "id:900110,\ +# phase:1,\ +# nolog,\ +# pass,\ +# t:none,\ +# setvar:tx.inbound_anomaly_score_threshold=5,\ +# setvar:tx.outbound_anomaly_score_threshold=4" + + +# +# -- [[ Application Specific Rule Exclusions ]] -------------------------------- +# +# CRS 3.x contained exclusion packages to tweak the CRS for use with common +# web applications, lowering the number of false positives. +# +# In CRS 4, these are no longer part of the CRS itself, but they are available +# as "CRS plugins". Some plugins improve support for web applications, and others +# may bring new functionality. Plugins are not installed by default, but can be +# downloaded from the plugin registry: +# https://github.com/coreruleset/plugin-registry +# +# For detailed information about using and installing plugins, please see: +# https://coreruleset.org/docs/configuring/plugins/ + + +# +# -- [[ Anomaly Score Reporting Level ]] --------------------------------------- +# +# When a request is blocked due to the anomaly score meeting or exceeding the +# anomaly threshold then the blocking rule will also report the anomaly score. +# This applies to the separate inbound and outbound anomaly scores. +# +# In phase 5, there are additional rules that can perform additional reporting +# of anomaly scores with a verbosity that depends on the reporting level defined +# below. +# +# By setting the reporting level you control whether you want additional +# reporting beyond the blocking rule or not and, if yes, which requests should +# be covered. The higher the reporting level, the more verbose the reporting is. +# +# There are 6 reporting levels: +# +# 0 - Reporting disabled +# 1 - Reporting for requests with a blocking anomaly score >= a threshold +# 2 - Reporting for requests with a detection anomaly score >= a threshold +# 3 - Reporting for requests with a blocking anomaly score greater than 0 +# 4 - Reporting for requests with a detection anomaly score greater than 0 +# 5 - Reporting for all requests +# +# Note: Reporting levels 1 and 2 make it possible to differentiate between +# requests that are blocked and requests that are *not* blocked but would have +# been blocked if the blocking PL was equal to detection PL. This may be useful +# for certain FP tuning methodologies, for example moving to a higher PL. +# +# A value of 5 can be useful on platforms where you are interested in logging +# non-scoring requests, yet it is not possible to report this information in +# the request/access log. This applies to Nginx, for example. +# +#SecAction \ +# "id:900115,\ +# phase:1,\ +# nolog,\ +# pass,\ +# t:none,\ +# setvar:tx.reporting_level=4" + + +# +# -- [[ Early Anomaly Scoring Mode Blocking ]] ------------------------------ +# +# The anomaly scores for the request and the responses are generally summed up +# and evaluated at the end of phase:2 and at the end of phase:4 respectively. +# However, it is possible to enable an early evaluation of these anomaly scores +# at the end of phase:1 and at the end of phase:3. +# +# If a request (or a response) hits the anomaly threshold in this early +# evaluation, then blocking happens immediately (if blocking is enabled) and +# the phase 2 (and phase 4 respectively) will no longer be executed. +# +# Enable the rule 900120 that sets the variable tx.early_blocking to 1 in order +# to enable early blocking. The variable tx.early_blocking is set to 0 by +# default. Early blocking is thus disabled by default. +# +# Please note that early blocking will hide potential alerts from you. This +# means that a payload that would appear in an alert in phase 2 (or phase 4) +# does not get evaluated if the request is being blocked early. So when you +# disabled early blocking again at some point in the future, then new alerts +# from phase 2 might pop up. +#SecAction \ +# "id:900120,\ +# phase:1,\ +# nolog,\ +# pass,\ +# t:none,\ +# setvar:tx.early_blocking=1" + + +# +# -- [[ HTTP Policy Settings ]] ------------------------------------------------ +# +# This section defines your policies for the HTTP protocol, such as: +# - allowed HTTP versions, HTTP methods, allowed request Content-Types +# - forbidden file extensions (e.g. .bak, .sql) and request headers (e.g. Proxy) +# +# These variables are used in the following rule files: +# - REQUEST-911-METHOD-ENFORCEMENT.conf +# - REQUEST-920-PROTOCOL-ENFORCEMENT.conf + +# HTTP methods that a client is allowed to use. +# Default: GET HEAD POST OPTIONS +# Example: for RESTful APIs, add the following methods: PUT PATCH DELETE +# Example: for WebDAV, add the following methods: CHECKOUT COPY DELETE LOCK +# MERGE MKACTIVITY MKCOL MOVE PROPFIND PROPPATCH PUT UNLOCK +# Uncomment this rule to change the default. +#SecAction \ +# "id:900200,\ +# phase:1,\ +# nolog,\ +# pass,\ +# t:none,\ +# setvar:'tx.allowed_methods=GET HEAD POST OPTIONS'" + +# Content-Types that a client is allowed to send in a request. +# Default: |application/x-www-form-urlencoded| |multipart/form-data| |multipart/related| +# |text/xml| |application/xml| |application/soap+xml| |application/x-amf| |application/json| +# |application/cloudevents+json| |application/cloudevents-batch+json| |application/octet-stream| +# |application/csp-report| |application/xss-auditor-report| |text/plain| +# Uncomment this rule to change the default. +# +# Please note, that the rule where CRS uses this variable (920420) evaluates it with operator +# `@within`, which is case sensitive, but uses t:lowercase. You must add your whole custom +# Content-Type with lowercase. +# +#SecAction \ +# "id:900220,\ +# phase:1,\ +# nolog,\ +# pass,\ +# t:none,\ +# setvar:'tx.allowed_request_content_type=|application/x-www-form-urlencoded| |multipart/form-data| |multipart/related| |text/xml| |application/xml| |application/soap+xml| |application/x-amf| |application/json| |application/cloudevents+json| |application/cloudevents-batch+json| |application/octet-stream| |application/csp-report| |application/xss-auditor-report| |text/plain|'" + +# Allowed HTTP versions. +# Default: HTTP/1.0 HTTP/1.1 HTTP/2 HTTP/2.0 +# Example for legacy clients: HTTP/0.9 HTTP/1.0 HTTP/1.1 HTTP/2 HTTP/2.0 +# Note that some web server versions use 'HTTP/2', some 'HTTP/2.0', so +# we include both version strings by default. +# Uncomment this rule to change the default. +#SecAction \ +# "id:900230,\ +# phase:1,\ +# nolog,\ +# pass,\ +# t:none,\ +# setvar:'tx.allowed_http_versions=HTTP/1.0 HTTP/1.1 HTTP/2 HTTP/2.0'" + +# Forbidden file extensions. +# Guards against unintended exposure of development/configuration files. +# Default: .asa/ .asax/ .ascx/ .backup/ .bak/ .bat/ .cdx/ .cer/ .cfg/ .cmd/ .com/ .config/ .conf/ .cs/ .csproj/ .csr/ .dat/ .db/ .dbf/ .dll/ .dos/ .htr/ .htw/ .ida/ .idc/ .idq/ .inc/ .ini/ .key/ .licx/ .lnk/ .log/ .mdb/ .old/ .pass/ .pdb/ .pol/ .printer/ .pwd/ .rdb/ .resources/ .resx/ .sql/ .swp/ .sys/ .vb/ .vbs/ .vbproj/ .vsdisco/ .webinfo/ .xsd/ .xsx/ +# Example: .bak/ .config/ .conf/ .db/ .ini/ .log/ .old/ .pass/ .pdb/ .rdb/ .sql/ +# Note that .axd was removed due to false positives (see PR 1925). +# +# To additionally guard against configuration/install archive files from being +# accidentally exposed, common archive file extensions can be added to the +# restricted extensions list. An example list of common archive file extensions +# is presented below: +# .7z/ .br/ .bz/ .bz2/ .cab/ .cpio/ .gz/ .img/ .iso/ .jar/ .rar/ .tar/ .tbz2/ .tgz/ .txz/ .xz/ .zip/ .zst/ +# (Source: https://en.wikipedia.org/wiki/List_of_archive_formats) +# +# Uncomment this rule to change the default. +#SecAction \ +# "id:900240,\ +# phase:1,\ +# nolog,\ +# pass,\ +# t:none,\ +# setvar:'tx.restricted_extensions=.asa/ .asax/ .ascx/ .backup/ .bak/ .bat/ .cdx/ .cer/ .cfg/ .cmd/ .com/ .config/ .conf/ .cs/ .csproj/ .csr/ .dat/ .db/ .dbf/ .dll/ .dos/ .htr/ .htw/ .ida/ .idc/ .idq/ .inc/ .ini/ .key/ .licx/ .lnk/ .log/ .mdb/ .old/ .pass/ .pdb/ .pol/ .printer/ .pwd/ .rdb/ .resources/ .resx/ .sql/ .swp/ .sys/ .vb/ .vbs/ .vbproj/ .vsdisco/ .webinfo/ .xsd/ .xsx/'" + +# Forbidden request headers. +# Header names should be lowercase, enclosed by /slashes/ as delimiters. +# Blocking Proxy header prevents 'httpoxy' vulnerability: https://httpoxy.org +# Default: /proxy/ /lock-token/ /content-range/ /if/ /user-agentt/ +# Uncomment this rule to change the default. +#SecAction \ +# "id:900250,\ +# phase:1,\ +# nolog,\ +# pass,\ +# t:none,\ +# setvar:'tx.restricted_headers=/proxy/ /lock-token/ /content-range/ /if/ /user-agentt/'" + +# Content-Types charsets that a client is allowed to send in a request. +# The content-types are enclosed by |pipes| as delimiters to guarantee exact matches. +# Default: |utf-8| |iso-8859-1| |iso-8859-15| |windows-1252| +# Uncomment this rule to change the default. +#SecAction \ +# "id:900280,\ +# phase:1,\ +# nolog,\ +# pass,\ +# t:none,\ +# setvar:'tx.allowed_request_content_type_charset=|utf-8| |iso-8859-1| |iso-8859-15| |windows-1252|'" + +# +# -- [[ HTTP Argument/Upload Limits ]] ----------------------------------------- +# +# Here you can define optional limits on HTTP get/post parameters and uploads. +# This can help to prevent application specific DoS attacks. +# +# These values are checked in REQUEST-920-PROTOCOL-ENFORCEMENT.conf. +# Beware of blocking legitimate traffic when enabling these limits. +# + +# Block request if number of arguments is too high +# Default: unlimited +# Example: 255 +# Uncomment this rule to set a limit. +#SecAction \ +# "id:900300,\ +# phase:1,\ +# nolog,\ +# pass,\ +# t:none,\ +# setvar:tx.max_num_args=255" + +# Block request if the length of any argument name is too high +# Default: unlimited +# Example: 100 +# Uncomment this rule to set a limit. +#SecAction \ +# "id:900310,\ +# phase:1,\ +# nolog,\ +# pass,\ +# t:none,\ +# setvar:tx.arg_name_length=100" + +# Block request if the length of any argument value is too high +# Default: unlimited +# Example: 400 +# Uncomment this rule to set a limit. +#SecAction \ +# "id:900320,\ +# phase:1,\ +# nolog,\ +# pass,\ +# t:none,\ +# setvar:tx.arg_length=400" + +# Block request if the total length of all combined arguments is too high +# Default: unlimited +# Example: 64000 +# Uncomment this rule to set a limit. +#SecAction \ +# "id:900330,\ +# phase:1,\ +# nolog,\ +# pass,\ +# t:none,\ +# setvar:tx.total_arg_length=64000" + +# Block request if the file size of any individual uploaded file is too high +# Default: unlimited +# Example: 1048576 +# Uncomment this rule to set a limit. +#SecAction \ +# "id:900340,\ +# phase:1,\ +# nolog,\ +# pass,\ +# t:none,\ +# setvar:tx.max_file_size=1048576" + +# Block request if the total size of all combined uploaded files is too high +# Default: unlimited +# Example: 1048576 +# Uncomment this rule to set a limit. +#SecAction \ +# "id:900350,\ +# phase:1,\ +# nolog,\ +# pass,\ +# t:none,\ +# setvar:tx.combined_file_sizes=1048576" + + +# +# -- [[ Easing In / Sampling Percentage ]] ------------------------------------- +# +# Adding the Core Rule Set to an existing productive site can lead to false +# positives, unexpected performance issues and other undesired side effects. +# +# It can be beneficial to test the water first by enabling the CRS for a +# limited number of requests only and then, when you have solved the issues (if +# any) and you have confidence in the setup, to raise the ratio of requests +# being sent into the ruleset. +# +# Adjust the percentage of requests that are funnelled into the Core Rules by +# setting TX.sampling_percentage below. The default is 100, meaning that every +# request gets checked by the CRS. The selection of requests, which are going +# to be checked, is based on a pseudo random number generated by ModSecurity. +# +# If a request is allowed to pass without being checked by the CRS, there is no +# entry in the audit log (for performance reasons), but an error log entry is +# written. If you want to disable the error log entry, then issue the +# following directive somewhere after the inclusion of the CRS +# (E.g., RESPONSE-999-EXCLUSION-RULES-AFTER-CRS.conf). +# +# SecRuleUpdateActionById 901450 "nolog" +# +# ATTENTION: If this TX.sampling_percentage is below 100, then some of the +# requests will bypass the Core Rules completely and you lose the ability to +# protect your service with ModSecurity. +# +# Uncomment this rule to enable this feature: +# +#SecAction "id:900400,\ +# phase:1,\ +# pass,\ +# nolog,\ +# setvar:tx.sampling_percentage=100" + + + +# +# -- [[ Check UTF-8 encoding ]] ------------------------------------------------ +# +# The CRS can optionally check request contents for invalid UTF-8 encoding. +# We only want to apply this check if UTF-8 encoding is actually used by the +# site; otherwise it will result in false positives. +# +# Uncomment this rule to use this feature: +# +#SecAction \ +# "id:900950,\ +# phase:1,\ +# nolog,\ +# pass,\ +# t:none,\ +# setvar:tx.crs_validate_utf8_encoding=1" + + +# +# -- [[ Collection timeout ]] -------------------------------------------------- +# +# Set the SecCollectionTimeout directive from the ModSecurity default (1 hour) +# to a lower setting which is appropriate to most sites. +# This increases performance by cleaning out stale collection (block) entries. +# +# This value should be greater than or equal to any block durations or timeouts +# set by plugins that make use of ModSecurity's persistent collections (e.g. the +# DoS protection and IP reputation plugins). +# +# Ref: https://github.com/SpiderLabs/ModSecurity/wiki/Reference-Manual-(v2.x)#SecCollectionTimeout + +# Please keep this directive uncommented. +# Default: 600 (10 minutes) +SecCollectionTimeout 600 + + +# +# -- [[ End of setup ]] -------------------------------------------------------- +# +# The CRS checks the tx.crs_setup_version variable to ensure that the setup +# has been loaded. If you are not planning to use this setup template, +# you must manually set the tx.crs_setup_version variable before including +# the CRS rules/* files. +# +# The variable is a numerical representation of the CRS version number. +# E.g., v3.0.0 is represented as 300. +# +SecAction \ + "id:900990,\ + phase:1,\ + nolog,\ + pass,\ + t:none,\ + setvar:tx.crs_setup_version=400"