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"