When ModSecurity appears to log attacks but not block them, we need to systematically verify its operational state. First check your configuration:
# Check if SecRuleEngine is On (not DetectionOnly)
SecRuleEngine On
# Verify the default action chain
SecDefaultAction "phase:2,deny,status:403,log,auditlog"
These test payloads should trigger immediate 403 responses when ModSecurity is properly configured:
# SQL Injection test (URL parameter)
curl -I "http://yoursite.com/search?q=1'+UNION+SELECT+password+FROM+users--"
# XSS test (form input)
curl -X POST -d "comment=<script>alert(1)</script>" http://yoursite.com/feedback
# Path traversal test
curl -I "http://yoursite.com/../../etc/passwd"
A proper ModSecurity block should return:
- HTTP 403 status code (not 404)
- ModSecurity-specific error message
- Corresponding audit log entry
If tests aren't blocking:
# 1. Verify rule processing order
grep -R "SecRule" /etc/modsecurity/
# 2. Check rule exclusion lists
cat /etc/modsecurity/crs/crs-setup.conf | grep "SecAction"
# 3. Test with OWASP CRS paranoia level 4
SecAction \
"id:900000,\
phase:1,\
nolog,\
pass,\
t:none,\
setvar:tx.paranoia_level=4"
Create a custom test rule for positive confirmation:
SecRule ARGS:testparam "@contains modsec_test" \
"id:999999,\
phase:2,\
deny,\
log,\
msg:'ModSecurity test rule triggered',\
tag:'application-multi',\
tag:'language-multi',\
tag:'platform-multi',\
tag:'attack-generic'"
Then test with: curl "http://yoursite.com/?testparam=modsec_test"
Many developers face this exact scenario after setting up ModSecurity - they see detection logs but can't confirm actual blocking functionality. The key indicators you should look for:
- 403 Forbidden responses instead of 404 pages
- Distinct ModSecurity error pages (when configured)
- Corresponding entries in audit logs showing "block" actions
Try these simple test vectors that should trigger ModSecurity's default rules:
# SQL Injection test
curl -v "http://yoursite.com/?param=1'%20OR%20'1'%20=%20'1"
# XSS test
curl -v "http://yoursite.com/?param=<script>alert(1)</script>"
# Path traversal test
curl -v "http://yoursite.com/../../etc/passwd"
Ensure your configuration has these critical settings:
# In modsecurity.conf
SecRuleEngine On
SecDefaultAction "phase:2,deny,status:403"
SecAuditEngine RelevantOnly
SecAuditLog /var/log/modsec_audit.log
Add this custom rule to force a block for testing:
SecRule ARGS:testparam "@contains testblock" \
"id:999999,\
phase:2,\
deny,\
status:403,\
msg:'Test block triggered',\
logdata:'Matched %{MATCHED_VAR}'"
Then test with:
curl -v "http://yoursite.com/?testparam=testblock"
When properly configured, your audit log should show entries like:
[timestamp] [unique_id] [client_ip] [block_action] [rule_id] [message]
[timestamp] [unique_id] [client_ip] [block_action] [rule_id] [message]
- Rule engine set to DetectionOnly mode
- Incorrect SecDefaultAction configuration
- Missing or misconfigured audit log settings
- Rules not properly loaded (check error logs)
For comprehensive testing, consider using the OWASP Core Rule Set (CRS) test cases:
git clone https://github.com/coreruleset/coreruleset
cd coreruleset/tests/regression/tests
./run-regression-tests.sh -h yoursite.com