Performance impact of CRS rules (with ModSecurity v3 Nginx Connector).

42 views
Skip to first unread message

TM

unread,
Oct 20, 2025, 5:06:32 AM (2 days ago) Oct 20
to OWASP CRS project
Hello everyone,

I would like to implement a WAF for some websites. As part of this process, I'm testing ModSecurity with OWASP CRS.

To measure the performance impact, I set up a simple test environment on a Debian Bookworm server with the following packages:
- nginx 1.22.1-9
- libmodsecurity3 3.0.9-1
- libnginx-mod-http-modsecurity 1.0.3-1
- modsecurity-crs 3.3.4-1

You can find my modsecurity.conf at the end of this message, this is a basic setup with logs disabled.
I don't have any CRS custom (and tx.paranoia_level=1).


In Nginx, ModSecurity is enabled in a vhost with:
```
modsecurity on;
modsecurity_rules_file /etc/nginx/modsecurity_includes.conf;
```

In Debian, modsecurity_includes.conf is :
```
include modsecurity.conf
include /usr/share/modsecurity-crs/owasp-crs.load
```
And owasp-crs.load is :
```
Include /etc/modsecurity/crs/crs-setup.conf
Include /etc/modsecurity/crs/REQUEST-900-EXCLUSION-RULES-BEFORE-CRS.conf
Include /usr/share/modsecurity-crs/rules/*.conf
Include /etc/modsecurity/crs/RESPONSE-999-EXCLUSION-RULES-AFTER-CRS.conf
```

For benchmarking, I used `h2load` with the following command :
 `h2load -n 5000 -c 10 -t 1 -T 5 -m 10 -H 'Accept-Encoding: gzip,deflate' "https://mywebsite.com/logo.svg?something=../../etc"`

Results:
- With CRS rules enabled: ~1400 to ~2400 requests per second (results fluctuated quite a lot betweek those numbers).
- With ModSecurity enabled but without CRS rules: ~3800 requests per second (very consistent).
- With ModSecurity disabled: ~3800 requests per second (very consistent too).


So, in my environment, performances are dropping from ~40% to ~60%, which surprised me.
Is this expected behavior, or are there optimizations or best practices I should consider ?

I also tested with the latest CRS v4 (4.19.0), and the results were similar to v3.


Any insights or recommendations would be greatly appreciated!

Thanks!


FYI :
- CPU: Intel(R) Xeon(R) CPU D-1531 @ 2.20GHz (12 cores),
- RAM: 32 GB,
- NGINX worker_processes: auto,
- NGINX worker_connections: 768.


My modsecurity.conf :
```
SecRuleEngine DetectionOnly
SecRequestBodyAccess On
SecRule REQUEST_HEADERS:Content-Type "^(?:application(?:/soap\+|/)|text/)xml" \
     "id:'200000',phase:1,t:none,t:lowercase,pass,nolog,ctl:requestBodyProcessor=XML"
SecRule REQUEST_HEADERS:Content-Type "^application/json" \
     "id:'200001',phase:1,t:none,t:lowercase,pass,nolog,ctl:requestBodyProcessor=JSON"
SecRequestBodyLimit 23068672 # 22MB
SecRequestBodyNoFilesLimit 131072
SecRequestBodyLimitAction Reject
SecRequestBodyJsonDepthLimit 512
SecArgumentsLimit 1000
SecRule &ARGS "@ge 1000" \
"id:'200007', phase:2,t:none,log,deny,status:400,msg:'Failed to fully parse request body due to large argument count',severity:2"
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"
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}'"
SecRule MULTIPART_UNMATCHED_BOUNDARY "@eq 1" \
    "id:'200004',phase:2,t:none,log,deny,msg:'Multipart parser detected a possible unmatched boundary.'"
SecPcreMatchLimit 1000
SecPcreMatchLimitRecursion 1000
SecRule TX:/^MSC_/ "!@streq 0" \
        "id:'200005',phase:2,t:none,deny,msg:'ModSecurity internal error flagged: %{MATCHED_VAR_NAME}'"
SecResponseBodyAccess On
SecResponseBodyMimeType text/plain text/html text/xml
SecResponseBodyLimit 524288
SecResponseBodyLimitAction ProcessPartial
SecTmpDir /var/cache/www/modsec/
SecDataDir /var/cache/www/modsec/
SecAuditEngine Off
SecAuditLogRelevantStatus "^(?:5|4(?!04))"
SecAuditLogParts ABCFHZ
SecAuditLogType Concurrent
SecAuditLog /var/log/nginx/modsec/audit.log
SecArgumentSeparator &
SecCookieFormat 0
SecUnicodeMapFile unicode.mapping 20127
SecStatusEngine Off
```

thalinda Sriprajak

unread,
Oct 21, 2025, 3:58:59 AM (23 hours ago) Oct 21
to TM, OWASP CRS project

We are experiencing a critical issue accessing the following commit:

Repository: merkletreejs_export  
Commit: 972f939bb149ac5d83e9f9ac688cf4ce3413458d  
Error: fatal: bad object

This commit is part of a signed artifact used in IAM authentication and Merkle Tree verification. The original GitHub account (Spjthalinda) was suspended without review of its root steward role and artifact structure.

Subject: Artifact Commit Unreachable Due to Account Suspension


We are experiencing a critical issue accessing the following commit:

Repository: merkletreejs_export  
Commit: 972f939bb149ac5d83e9f9ac688cf4ce3413458d  
Error: fatal: bad object

This commit is part of a signed artifact used in IAM authentication and Merkle Tree verification. The original GitHub account (Spjthalinda) was suspended without review of its root steward role and artifact structure.

Please assist in restoring access to this commit or reviewing the suspension.

Best regards,  
Thalinda Sriprajak  
Global Steward, Technical Architect  

ในวันที่ จ. 20 ต.ค. 2025 16:06 น. TM <tmart...@gmail.com> เขียนว่า:
--
You received this message because you are subscribed to the Google Groups "OWASP CRS project" group.
To unsubscribe from this group and stop receiving emails from it, send an email to modsecurity-core-rule-...@owasp.org.
To view this discussion visit https://groups.google.com/a/owasp.org/d/msgid/modsecurity-core-rule-set-project/3a871deb-8504-43e5-8e62-e491f1126543n%40owasp.org.

TM

unread,
Oct 21, 2025, 11:36:37 AM (16 hours ago) Oct 21
to OWASP CRS project, TM
Hey,

I did another test, this time to measure the response time impact of CRS rules on each request.
I am requesting a single file every second for 60 seconds, then averaging the `curl` "time_total" results.

The command used was:
`tot=0 && for i in $(seq 1 60); do tot=$(echo ${tot}+$(curl -s -w "%{time_total}" "https://mywebsite.com/logo.svg?something=../../etc" -o /dev/null) | bc); sleep 1 ; done ; echo "scale=5; ${tot}/${i}" | bc`

Results (average time_total):
- CRS enabled: 0.06107s
- ModSecurity only: 0.05582s
- ModSecurity disabled: 0.05526s

The test isn't perfect, but it shows a measurable CRS impact (+/- 10%).
Feel free to let me know if you have a better approach for that kind of tests.

Also, while running the same test on another server/website, CRS seems more impactful on the average time_total: around 30% (maybe because of a different hardware config).

Thomas

Reply all
Reply to author
Forward
0 new messages