WAF Rules (ModSecurity)¶
Installation¶
Ubuntu / Debian (Nginx)¶
ModSecurity as a standalone or Nginx module:
# Install libmodsecurity3
apt install -y libmodsecurity3
# Or compile with Nginx (dynamic module)
git clone --depth 1 https://github.com/SpiderLabs/ModSecurity-nginx.git
Docker with ModSecurity¶
docker-compose.yml
services:
nginx:
image: owasp/modsecurity-crs:nginx
container_name: waf
restart: unless-stopped
ports:
- "80:80"
- "443:443"
environment:
PARANOIA: 2
BACKEND: http://app:3000
PORT: 80
volumes:
- ./nginx/custom.conf:/etc/nginx/conf.d/custom.conf
OWASP Core Rule Set (CRS)¶
The OWASP CRS is the standard set of WAF rules.
Manual Install¶
# Download CRS
git clone https://github.com/coreruleset/coreruleset /etc/modsecurity/crs
# Copy example config
cp /etc/modsecurity/crs/crs-setup.conf.example /etc/modsecurity/crs/crs-setup.conf
Enable in ModSecurity¶
/etc/modsecurity/modsecurity.conf
SecRuleEngine On
# Include CRS
Include /etc/modsecurity/crs/crs-setup.conf
Include /etc/modsecurity/crs/rules/*.conf
Rule Categories¶
| Category | Rules | Protection |
|---|---|---|
REQUEST-901-INITIALIZATION |
Initialization | Setup |
REQUEST-903.9001-DRUPAL-EXCLUSION |
Drupal exclusions | Reduce false positives |
REQUEST-903.9002-WORDPRESS-EXCLUSION |
WordPress exclusions | Reduce false positives |
REQUEST-905-COMMON-EXCEPTIONS |
Common exceptions | Plugin/file exclusions |
REQUEST-910-IP-REPUTATION |
IP reputation | Block malicious IPs |
REQUEST-911-METHOD-ENFORCEMENT |
HTTP methods | Block invalid methods |
REQUEST-912-DOS-PROTECTION |
DoS protection | Rate limiting |
REQUEST-913-SCANNER-DETECTION |
Scanner detection | Block security scanners |
REQUEST-920-PROTOCOL-ENFORCEMENT |
Protocol enforcement | HTTP validation |
REQUEST-921-PROTOCOL-ATTACK |
Protocol attack | Protocol abuse detection |
REQUEST-930-APPLICATION-ATTACK-LFI |
LFI | Local file inclusion |
REQUEST-931-APPLICATION-ATTACK-RFI |
RFI | Remote file inclusion |
REQUEST-932-APPLICATION-ATTACK-RCE |
RCE | Remote code execution |
REQUEST-933-APPLICATION-ATTACK-PHP |
PHP injection | PHP-specific attacks |
REQUEST-934-APPLICATION-ATTACK-NODEJS |
Node.js injection | Node.js attacks |
REQUEST-941-APPLICATION-ATTACK-XSS |
XSS | Cross-site scripting |
REQUEST-942-APPLICATION-ATTACK-SQLI |
SQLi | SQL injection |
REQUEST-943-APPLICATION-ATTACK-SESSION-FIXATION |
Session | Session fixation |
REQUEST-944-APPLICATION-ATTACK-JAVA |
Java | Java attacks |
REQUEST-949-BLOCKING-EVALUATION |
Blocking | Final blocking eval |
RESPONSE-950-DATA-LEAKAGES |
Data leak | Information disclosure |
RESPONSE-951-DATA-LEAKAGES-SQL |
SQL leak | SQL errors |
RESPONSE-952-DATA-LEAKAGES-JAVA |
Java leak | Java errors |
RESPONSE-953-DATA-LEAKAGES-PHP |
PHP leak | PHP errors |
RESPONSE-954-DATA-LEAKAGES-IIS |
IIS leak | IIS errors |
RESPONSE-959-BLOCKING-EVALUATION |
Blocking | Response blocking eval |
RESPONSE-980-CORRELATION |
Correlation | Alert correlation |
Paranoia Levels¶
| Level | Description | False Positives |
|---|---|---|
| 1 | Basic rules (default) | Low |
| 2 | Advanced rules | Medium |
| 3 | Aggressive rules | High |
| 4 | Extreme rules | Very high |
# Set paranoia level
PARANOIA=2
Custom Rules¶
/etc/modsecurity/custom-rules.conf
# Block specific IP
SecRule REMOTE_ADDR "^123\.45\.67\.89$" "id:1000,phase:1,deny,msg:'Blocked malicious IP'"
# Block user-agent
SecRule REQUEST_HEADERS:User-Agent "nikto|sqlmap" "id:1001,phase:1,deny,msg:'Blocked malicious scanner'"
# Block wp-login brute force (rate limit)
SecRule IP:BRUTE_FORCE_BLOCK "@streq 1" "id:1002,phase:1,deny,msg:'Brute force blocked'"
SecAction "id:1003,phase:5,initcol:ip=%{REMOTE_ADDR},pass,nolog"
SecRule REQUEST_FILENAME "/wp-login\.php" "id:1004,phase:2,t:none,setvar:ip.bf_counter=+1,expirevar:ip.bf_counter=60,pass,nolog"
SecRule IP:BF_COUNTER "@gt 5" "id:1005,phase:2,t:none,setvar:ip.brute_force_block=1,expirevar:ip.brute_force_block=300,msg:'Brute force detected'"
# Whitelist Cloudflare IPs
SecRule REMOTE_ADDR "@ipMatch 173.245.48.0/20,103.21.244.0/22" "id:1006,phase:1,allow,msg:'Cloudflare whitelist'"
ModSecurity + Nginx¶
server {
# Enable ModSecurity
modsecurity on;
modsecurity_rules_file /etc/modsecurity/modsecurity.conf;
location / {
proxy_pass http://127.0.0.1:3000;
}
}
Logging & Monitoring¶
# Audit log
tail -f /var/log/modsec_audit.log
# Check for blocked requests
grep "Blocked" /var/log/modsec_audit.log
# Count by rule ID
grep -oP 'id:"\K[^"]+' /var/log/modsec_audit.log | sort | uniq -c | sort -rn
Tuning (Reduce False Positives)¶
Add Exclusions¶
# WordPress admin exclusion
SecRule REQUEST_FILENAME "@beginsWith /wp-admin" \
"id:2000,phase:1,pass,nolog,ctl:ruleEngine=Off"
Change to Detection-Only¶
SecRuleEngine DetectionOnly
Whitelist Specific Parameters¶
# Allow HTML in post_content
SecRuleUpdateTargetById 942100 "!ARGS:post_content"
SecRuleUpdateTargetById 942110 "!ARGS:post_content"
Test Your WAF¶
# Test SQL injection
curl "https://example.com/?id=1' OR '1'='1"
# Test XSS
curl "https://example.com/?q=<script>alert(1)</script>"
# Test LFI
curl "https://example.com/?file=../../../etc/passwd"
# Should all return 403 Forbidden
Verification¶
- ModSecurity module loaded (
nginx -V 2>&1 | grep modsecurity) - OWASP CRS installed and enabled
- Paranoia level set (1-4)
- Audit logging enabled
- WordPress exclusions configured (if needed)
- False positives reviewed and tuned
- Test attacks blocked (403)
- Mode: DetectionOnly initially, then switched to On