Building a Production WAF with Apache HTTPS, ModSecurity, and OWASP CRS
Introduction
Web applications face relentless attacks from automated bots and malicious actors probing for vulnerabilities. SQL injection, cross-site scripting (XSS), and remote code execution attempts occur thousands of times per day against typical production systems. Traditional network firewalls operating at layers 3 and 4 cannot inspect HTTP semantics or detect application-layer attacks hidden within legitimate-looking requests.
This is where a Web Application Firewall (WAF) becomes essential. ModSecurity, the world’s most widely deployed open-source WAF, operates at layer 7 of the OSI model and examines HTTP request and response content in real time. When integrated with Apache HTTPS and the OWASP Core Rule Set (CRS), it provides enterprise-grade protection against the OWASP Top 10 vulnerabilities and many other attack vectors.
In this comprehensive guide, you’ll learn how to implement a production-ready WAF by installing ModSecurity 2.9 on Apache with SSL/TLS support, configuring the latest OWASP CRS rules, handling false positives through systematic tuning, and optimizing performance for high-traffic environments. By the end, you’ll have a robust security layer protecting your web applications from sophisticated attacks while maintaining excellent performance.
Prerequisites
Before beginning this implementation, ensure you have:
- Linux Server: Ubuntu 20.04 LTS or Debian 11+ (other distributions work with minor adjustments)
- Root or sudo access: Required for installing packages and configuring Apache
- Apache 2.4+: Already installed and running (we’ll verify and configure SSL)
- Basic Apache knowledge: Understanding of virtual hosts and configuration files
- SSL certificates: Valid certificates for HTTPS (Let’s Encrypt or commercial)
- Command-line proficiency: Comfortable with SSH, text editors (nano/vim), and bash commands
- Basic understanding of web security: Familiarity with common attacks (SQLi, XSS) is helpful
System requirements:
- Minimum 2GB RAM (4GB+ recommended for production)
- At least 2 CPU cores
- 10GB available disk space
- Network connectivity for downloading packages
Understanding the Architecture
Before diving into implementation, let’s understand how these components work together.
The flow explained:
- Client request arrives via HTTPS at Apache
- Apache SSL/TLS layer decrypts the connection
- ModSecurity engine intercepts the request before it reaches your application
- OWASP CRS rules analyze the request content for attack patterns
- Decision point: If malicious patterns are detected, the request is blocked (403 error); otherwise, it passes through
- Response inspection: ModSecurity can also examine outbound traffic for data leakage
- Logging: All actions are logged for security analysis and tuning
ModSecurity operates in different modes:
- DetectionOnly: Logs threats but allows all traffic (ideal for initial tuning)
- On: Actively blocks malicious requests (production mode)
- Off: Disabled (not recommended in production)
Step 1: Installing Apache and Enabling SSL/TLS
First, ensure Apache is properly installed with SSL support. If you already have Apache configured with HTTPS, you can skip to the SSL verification section.
Installing Apache with SSL Module
# Update package repositories
sudo apt update
# Install Apache and SSL module
sudo apt install apache2 apache2-utils -y
# Enable required Apache modules
sudo a2enmod ssl headers rewrite
sudo a2enmod socache_shmcb # For SSL session caching
# Restart Apache to load modules
sudo systemctl restart apache2
# Verify Apache is running
sudo systemctl status apache2
Configuring SSL/TLS for Apache
For production environments, obtain certificates from Let’s Encrypt or a commercial certificate authority. For testing, you can use self-signed certificates:
# Create directory for SSL certificates
sudo mkdir -p /etc/apache2/ssl
# Generate self-signed certificate (TESTING ONLY)
sudo openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
-keyout /etc/apache2/ssl/apache-selfsigned.key \
-out /etc/apache2/ssl/apache-selfsigned.crt
# Set proper permissions
sudo chmod 600 /etc/apache2/ssl/apache-selfsigned.key
Creating an HTTPS Virtual Host
Create a secure virtual host configuration:
# Create SSL virtual host configuration
sudo nano /etc/apache2/sites-available/default-ssl.conf
Add the following configuration:
<VirtualHost *:443>
ServerAdmin webmaster@localhost
ServerName yourdomain.com
DocumentRoot /var/www/html
# SSL Configuration
SSLEngine on
SSLCertificateFile /etc/apache2/ssl/apache-selfsigned.crt
SSLCertificateKeyFile /etc/apache2/ssl/apache-selfsigned.key
# Strong SSL/TLS configuration
SSLProtocol all -SSLv3 -TLSv1 -TLSv1.1
SSLCipherSuite HIGH:!aNULL:!MD5:!3DES
SSLHonorCipherOrder on
# Security Headers
Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"
Header always set X-Content-Type-Options "nosniff"
Header always set X-Frame-Options "SAMEORIGIN"
# Logging
ErrorLog ${APACHE_LOG_DIR}/ssl_error.log
CustomLog ${APACHE_LOG_DIR}/ssl_access.log combined
<Directory /var/www/html>
Options -Indexes +FollowSymLinks
AllowOverride All
Require all granted
</Directory>
</VirtualHost>
Enable the SSL site and test:
# Enable SSL virtual host
sudo a2ensite default-ssl
# Test Apache configuration
sudo apache2ctl configtest
# If syntax is OK, reload Apache
sudo systemctl reload apache2
# Verify HTTPS is working
curl -k https://localhost # -k ignores self-signed cert warnings
Step 2: Installing ModSecurity
ModSecurity 2.9 is the recommended version for Apache as it’s mature, stable, and highly optimized for the Apache architecture.
Installing from Package Repository
# Install ModSecurity and dependencies
sudo apt install libapache2-mod-security2 -y
# Verify installation
apachectl -M | grep security
You should see security2_module (shared) in the output, confirming ModSecurity is loaded.
Understanding ModSecurity File Structure
After installation, ModSecurity creates several important directories:
/etc/modsecurity/- Main configuration directory/usr/share/modsecurity-crs/- OWASP Core Rule Set location (if installed via package)/var/log/apache2/modsec_audit.log- Audit log file/var/cache/modsecurity/- Temporary data storage
Step 3: Configuring ModSecurity
ModSecurity ships with a recommended configuration template that needs to be activated and customized.
Activating the Configuration File
# Copy the recommended configuration to active configuration
sudo cp /etc/modsecurity/modsecurity.conf-recommended \
/etc/modsecurity/modsecurity.conf
# Edit the configuration
sudo nano /etc/modsecurity/modsecurity.conf
Critical Configuration Changes
Make these essential modifications:
1. Enable Active Blocking (Change from DetectionOnly to On)
Find this line:
SecRuleEngine DetectionOnly
Change it to:
SecRuleEngine On
Note: For initial deployment, keep it as DetectionOnly until you’ve tuned false positives. Change to On only after thorough testing.
2. Configure Audit Logging
Find and modify these directives:
# Enable audit logging for relevant events only
SecAuditEngine RelevantOnly
# Define what gets logged (change line 186)
# This corrects the default incorrect setting
SecAuditLogParts ABIJDEFHZ
# Audit log location
SecAuditLog /var/log/apache2/modsec_audit.log
# Use concurrent logging for better performance
SecAuditLogType Concurrent
SecAuditLogStorageDir /var/log/apache2/audit/
# Create the audit storage directory
sudo mkdir -p /var/log/apache2/audit/
sudo chown www-data:www-data /var/log/apache2/audit/
Explanation of audit log parts:
- A: Audit log header
- B: Request headers
- I: Request body (compact)
- J: Request body (JSON format)
- D: Reserved for intermediary response headers
- E: Intermediary response body
- F: Final response headers
- H: Audit log trailer
- Z: Final boundary
3. Configure Request/Response Body Limits
# Maximum request body size (default 128KB, adjust as needed)
SecRequestBodyLimit 13107200 # 12.5MB
# Maximum request body in-memory buffer
SecRequestBodyNoFilesLimit 131072 # 128KB
# Limit for how much of the response to buffer
SecResponseBodyLimit 524288 # 512KB
4. Disable Response Body Inspection (Performance Optimization)
# Disable by default, enable only for specific locations if needed
SecResponseBodyAccess Off
This significantly improves performance. Only enable response body inspection if you need data leakage protection.
5. Configure Temporary Directory
# Set data directory for persistent storage
SecDataDir /var/cache/modsecurity
# Verify directory exists and has correct permissions
sudo mkdir -p /var/cache/modsecurity
sudo chown www-data:www-data /var/cache/modsecurity
Configuring ModSecurity in Apache
Edit the ModSecurity Apache module configuration:
sudo nano /etc/apache2/mods-enabled/security2.conf
Ensure it contains:
<IfModule security2_module>
# ModSecurity Core Configuration
SecDataDir /var/cache/modsecurity
# Include ModSecurity configuration
IncludeOptional /etc/modsecurity/*.conf
# Include OWASP CRS (we'll add this after CRS installation)
# IncludeOptional /etc/modsecurity/crs-setup.conf
# IncludeOptional /etc/modsecurity/rules/*.conf
</IfModule>
Test and restart Apache:
# Test configuration
sudo apache2ctl configtest
# Restart Apache (reload isn't sufficient for ModSecurity changes)
sudo systemctl restart apache2
Step 4: Installing OWASP Core Rule Set (CRS)
The OWASP Core Rule Set is the standard, community-maintained rule set that provides comprehensive protection against web application attacks.
Checking for Existing CRS Installation
On Debian 12 and recent Ubuntu versions, CRS may already be installed:
# Check if CRS package is installed
dpkg -l | grep modsecurity-crs
If modsecurity-crs is already installed, skip manual installation and proceed to configuration.
Manual CRS Installation (Latest Version)
For the latest CRS version or if not installed via package:
# Install Git if not present
sudo apt install git -y
# Download CRS from GitHub
cd /tmp
git clone https://github.com/coreruleset/coreruleset.git
# Navigate to the cloned directory
cd coreruleset
# Check out the latest stable release (4.x as of 2024)
git checkout v4.7.0 # Replace with latest stable version
# Copy CRS setup configuration
sudo cp crs-setup.conf.example /etc/modsecurity/crs-setup.conf
# Copy all CRS rules
sudo cp -r rules /etc/modsecurity/
# Set proper ownership
sudo chown -R root:root /etc/modsecurity/crs-setup.conf
sudo chown -R root:root /etc/modsecurity/rules/
Understanding CRS Structure
The CRS consists of:
- crs-setup.conf: Main configuration file for CRS settings
- rules/: Directory containing all rule files organized by attack category:
- REQUEST-901-INITIALIZATION.conf
- REQUEST-911-METHOD-ENFORCEMENT.conf
- REQUEST-920-PROTOCOL-ENFORCEMENT.conf
- REQUEST-921-PROTOCOL-ATTACK.conf
- REQUEST-930-APPLICATION-ATTACK-LFI.conf
- REQUEST-931-APPLICATION-ATTACK-RFI.conf
- REQUEST-932-APPLICATION-ATTACK-RCE.conf
- REQUEST-933-APPLICATION-ATTACK-PHP.conf
- REQUEST-941-APPLICATION-ATTACK-XSS.conf
- REQUEST-942-APPLICATION-ATTACK-SQLI.conf
- REQUEST-943-APPLICATION-ATTACK-SESSION-FIXATION.conf
- And many more…
Configuring CRS
Edit the CRS setup configuration:
sudo nano /etc/modsecurity/crs-setup.conf
Key configurations to review:
1. Set Paranoia Level
# Paranoia Level (PL1-PL4)
# PL1: Default, lowest false positives
# PL2: More aggressive
# PL3: Very strict
# PL4: Maximum protection, highest false positives
#
# Start with PL1 for production
SecAction \
"id:900000,\
phase:1,\
nolog,\
pass,\
t:none,\
setvar:tx.paranoia_level=1"
2. Configure Anomaly Scoring
CRS uses anomaly scoring: each rule match adds to a score, and requests are blocked when the score exceeds a threshold.
# Inbound anomaly score threshold
# Default: 5 (recommended for production after tuning)
# During tuning: set higher (e.g., 1000) to log without blocking
SecAction \
"id:900110,\
phase:1,\
nolog,\
pass,\
t:none,\
setvar:tx.inbound_anomaly_score_threshold=5,\
setvar:tx.outbound_anomaly_score_threshold=4"
Scoring guide:
- Critical: 5 points
- Error: 4 points
- Warning: 3 points
- Notice: 2 points
3. Configure Allowed HTTP Methods
# Restrict HTTP methods to only those your application needs
SecAction \
"id:900200,\
phase:1,\
nolog,\
pass,\
t:none,\
setvar:'tx.allowed_methods=GET HEAD POST OPTIONS'"
4. Enable CRS in Apache Configuration
Update the ModSecurity Apache configuration:
sudo nano /etc/apache2/mods-enabled/security2.conf
Uncomment or add these lines:
<IfModule security2_module>
SecDataDir /var/cache/modsecurity
IncludeOptional /etc/modsecurity/*.conf
# Include OWASP CRS
Include /etc/modsecurity/crs-setup.conf
Include /etc/modsecurity/rules/*.conf
</IfModule>
Testing the Configuration
# Test Apache configuration syntax
sudo apache2ctl configtest
# If you get "ModSecurity: Found another rule with the same id" error:
# Comment out this line in security2.conf:
# IncludeOptional /etc/modsecurity/*.conf
# Keep only the explicit Include directives
# Restart Apache
sudo systemctl restart apache2
# Check Apache error log for any issues
sudo tail -f /var/log/apache2/error.log
Step 5: Testing ModSecurity Protection
Now let’s verify that ModSecurity is actively protecting your server.
Basic XSS Attack Test
# Test with a simple XSS attack pattern
curl "https://localhost/test.html?param=<script>alert('XSS')</script>" -k
If ModSecurity is working correctly, you should receive a 403 Forbidden response.
SQL Injection Test
# Test SQL injection pattern
curl "https://localhost/index.php?id=1' OR '1'='1" -k
Again, expect a 403 Forbidden response.
Checking the Audit Log
# View the audit log for detected attacks
sudo tail -50 /var/log/apache2/modsec_audit.log
# Or use grep to find specific rule triggers
sudo grep -i "id \"941" /var/log/apache2/modsec_audit.log
Creating a Custom Test Rule
For more controlled testing, create a custom rule:
# Create custom rules file
sudo nano /etc/modsecurity/custom-rules.conf
Add a simple test rule:
# Test rule to trigger on specific parameter
SecRule ARGS:testparam "@contains test" \
"id:100001,\
phase:2,\
deny,\
status:403,\
log,\
msg:'Custom test rule triggered'"
Include it in your Apache configuration:
sudo nano /etc/apache2/mods-enabled/security2.conf
Add before the CRS includes:
Include /etc/modsecurity/custom-rules.conf
Restart Apache and test:
sudo systemctl restart apache2
# This should trigger the rule and return 403
curl "https://localhost/?testparam=test" -k
# This should succeed
curl "https://localhost/?testparam=safe" -k
Step 6: Tuning False Positives
False positives are inevitable when first deploying ModSecurity. Systematic tuning is essential for production use.
Tuning Strategy
Follow this proven three-step approach:
- Start with DetectionOnly mode and high anomaly thresholds
- Collect baseline data from legitimate traffic (1-2 weeks)
- Progressively tighten by addressing false positives one at a time
Identifying False Positives
# Monitor audit log in real-time
sudo tail -f /var/log/apache2/modsec_audit.log
# Search for specific rule IDs
sudo grep "id \"942190\"" /var/log/apache2/modsec_audit.log
# Count occurrences of each rule
sudo grep -oP 'id "\K[0-9]+' /var/log/apache2/modsec_audit.log | \
sort | uniq -c | sort -rn | head -20
Methods for Handling False Positives
Method 1: Disable Rule Globally (Least Recommended)
# In custom-rules.conf
SecRuleRemoveById 942190
Use this only if the rule genuinely doesn’t apply to your application.
Method 2: Disable Rule for Specific URIs (Better)
# Disable SQL injection rule only for admin login
<LocationMatch "/admin/login">
SecRuleRemoveById 942190
</LocationMatch>
Method 3: Disable Rule for Specific Parameters (Best)
# Disable rule 942190 only for password fields on login
SecRule REQUEST_URI "@beginsWith /login" \
"id:100002,\
phase:1,\
nolog,\
pass,\
ctl:ruleRemoveTargetById=942190;ARGS:password"
Method 4: Runtime Rule Exclusion with Conditions
# Disable rule 942190 when URI is /submit-password
SecRule REQUEST_URI "@beginsWith /submit-password" \
"id:100003,\
phase:1,\
nolog,\
pass,\
ctl:ruleRemoveById=942190"
Creating a Whitelist Configuration
Organize your exclusions in a dedicated file:
sudo nano /etc/modsecurity/whitelist.conf
Example whitelist configuration:
# Whitelist Configuration for Production Application
# Last updated: 2024-12-17
# WordPress Admin Area - Allow file uploads
<LocationMatch "^/wp-admin/async-upload\.php">
SecRuleRemoveById 920420
SecRuleRemoveById 920470
</LocationMatch>
# API Endpoints - JSON in request bodies
SecRule REQUEST_URI "@beginsWith /api/" \
"id:100010,\
phase:1,\
nolog,\
pass,\
ctl:ruleRemoveTargetById=942432;REQUEST_BODY"
# User profile updates - Allow special characters in bio
SecRule REQUEST_URI "@beginsWith /profile/update" \
"id:100011,\
phase:1,\
nolog,\
pass,\
ctl:ruleRemoveTargetById=941100;ARGS:bio,\
ctl:ruleRemoveTargetById=941160;ARGS:bio"
# Payment gateway callback - Complex parameter values
SecRule REMOTE_ADDR "@ipMatch 203.0.113.0/24" \
"id:100012,\
phase:1,\
nolog,\
pass,\
ctl:ruleRemoveById=942100"
Include this in your configuration:
# In /etc/apache2/mods-enabled/security2.conf
Include /etc/modsecurity/whitelist.conf
Monitoring and Iterative Tuning
# Create a monitoring script
sudo nano /usr/local/bin/modsec-monitor.sh
#!/bin/bash
# ModSecurity monitoring script
LOG_FILE="/var/log/apache2/modsec_audit.log"
DATE=$(date +%Y-%m-%d)
echo "=== ModSecurity Activity Report for $DATE ==="
echo
echo "Top 10 Triggered Rules:"
grep -oP 'id "\K[0-9]+' "$LOG_FILE" | \
sort | uniq -c | sort -rn | head -10
echo
echo "Top 10 Blocked URIs:"
grep -oP 'uri "\K[^"]+' "$LOG_FILE" | \
sort | uniq -c | sort -rn | head -10
echo
echo "Top 10 Source IPs:"
grep -oP 'client-ip=\K[0-9.]+' "$LOG_FILE" | \
sort | uniq -c | sort -rn | head -10
Make it executable and run daily:
sudo chmod +x /usr/local/bin/modsec-monitor.sh
sudo /usr/local/bin/modsec-monitor.sh
Step 7: Performance Optimization
ModSecurity adds processing overhead. Here’s how to optimize for production.
Optimization Techniques
1. Disable Response Body Inspection
Already covered in configuration, but ensure it’s set:
SecResponseBodyAccess Off
Enable only for specific locations requiring data leakage protection:
<LocationMatch "/sensitive-api/">
SecResponseBodyAccess On
</LocationMatch>
2. Use Concurrent Audit Logging
SecAuditLogType Concurrent
SecAuditLogStorageDir /var/log/apache2/audit/
This prevents file locking bottlenecks.
3. Exclude Static Content
# Don't inspect images, CSS, JS files
SecRule REQUEST_URI "@rx \.(jpg|jpeg|png|gif|css|js|ico|svg|woff|woff2|ttf)$" \
"id:100020,\
phase:1,\
nolog,\
pass,\
ctl:ruleEngine=Off"
4. Optimize Persistent Storage
# Reduce collection timeout from default 3600 seconds
SecCollectionTimeout 600
# Use RAM disk for temporary storage (Linux)
# Add to /etc/fstab:
# tmpfs /var/cache/modsecurity tmpfs defaults,size=512M 0 0
5. Enable PCRE JIT Compilation
When compiling ModSecurity from source:
# Ensure PCRE is compiled with JIT support
./configure --enable-pcre-jit
For package installations, this is typically already enabled.
6. Limit Audit Logging
# Log only relevant events (not every request)
SecAuditEngine RelevantOnly
# Reduce audit log parts
SecAuditLogParts ABIJFHZ # Removed D, E (response bodies)
# Set log rotation
# In /etc/logrotate.d/apache2
/var/log/apache2/modsec_audit.log {
daily
rotate 14
compress
delaycompress
notifempty
create 640 root adm
sharedscripts
postrotate
/usr/sbin/apache2ctl graceful > /dev/null
endscript
}
Performance Testing
# Benchmark without ModSecurity
sudo apache2ctl -M | grep security
# If loaded, temporarily disable:
sudo a2dismod security2
sudo systemctl restart apache2
# Run Apache Bench
ab -n 10000 -c 100 https://localhost/
# Re-enable ModSecurity
sudo a2enmod security2
sudo systemctl restart apache2
# Test with ModSecurity enabled
ab -n 10000 -c 100 https://localhost/
# Compare results
Typical performance impact with properly tuned ModSecurity: 5-15% reduction in requests/second.
Step 8: Production Deployment Checklist
Before going live with ModSecurity in blocking mode:
### Pre-Production Checklist
- [ ] ModSecurity running in DetectionOnly for 1-2 weeks minimum
- [ ] Audit logs reviewed and false positives addressed
- [ ] Whitelist rules documented and version controlled
- [ ] Anomaly threshold tested (recommended: 5 for production)
- [ ] Performance benchmarks acceptable (< 20% overhead)
- [ ] Monitoring and alerting configured
- [ ] Log rotation configured
- [ ] Backup and rollback plan documented
- [ ] Team trained on reading audit logs
- [ ] Incident response procedure defined
### Production Deployment
- [ ] Change SecRuleEngine to On
- [ ] Set final anomaly thresholds
- [ ] Configure alerting for critical events
- [ ] Document all custom rules and exclusions
- [ ] Schedule regular rule set updates
- [ ] Plan quarterly review of audit logs
Enabling Blocking Mode
# Edit ModSecurity configuration
sudo nano /etc/modsecurity/modsecurity.conf
Change:
SecRuleEngine On # Was DetectionOnly
Restart Apache:
sudo apache2ctl configtest
sudo systemctl restart apache2
Monitor closely for the first 24-48 hours:
# Watch for blocked legitimate requests
sudo tail -f /var/log/apache2/modsec_audit.log | grep -i deny
Common Pitfalls and Troubleshooting
Issue 1: Apache Won’t Start After Adding ModSecurity
Symptoms: Apache fails to start, error log shows rule ID conflicts
Solution:
# Check for duplicate rule IDs
sudo grep -r "id:" /etc/modsecurity/rules/ | cut -d: -f3 | sort | uniq -d
# Common cause: IncludeOptional loads rules twice
# Edit security2.conf and use explicit Include instead:
Include /etc/modsecurity/crs-setup.conf
Include /etc/modsecurity/rules/*.conf
# Remove: IncludeOptional /etc/modsecurity/*.conf
Issue 2: All POST Requests Blocked
Symptoms: Forms and API calls return 403
Cause: Request body limit too low or body processor not configured
Solution:
# Increase request body limits in modsecurity.conf
SecRequestBodyLimit 13107200 # 12.5MB
SecRequestBodyNoFilesLimit 131072 # 128KB
# For JSON APIs, ensure body processor is set
SecRule REQUEST_HEADERS:Content-Type "@rx ^application/json" \
"id:100030,\
phase:1,\
nolog,\
pass,\
ctl:requestBodyProcessor=JSON"
Issue 3: High Memory Usage
Symptoms: Apache processes consuming excessive RAM
Causes:
- Response body buffering enabled globally
- Too much audit logging
- Persistent storage not cleaned
Solutions:
# Disable response body inspection
SecResponseBodyAccess Off
# Reduce audit logging
SecAuditLogParts ABIJFHZ # Remove D, E
# Clean persistent storage regularly
SecCollectionTimeout 600
# Limit concurrent connections in Apache
<IfModule mpm_prefork_module>
MaxRequestWorkers 150 # Adjust based on RAM
</IfModule>
Issue 4: False Positives from WordPress/CMS
Symptoms: Admin functions blocked, plugin updates fail
Solution: Use application-specific exclusions
# WordPress exclusions example
<LocationMatch "^/wp-admin/admin-ajax\.php">
SecRuleRemoveById 920420 920470 942100
</LocationMatch>
<LocationMatch "^/wp-admin/post\.php">
SecRuleRemoveById 941100 941160 941180
</LocationMatch>
# WooCommerce cart
<LocationMatch "^/\?wc-ajax=">
SecRuleRemoveById 942100 942260 942370
</LocationMatch>
Issue 5: Slow Performance
Diagnostics:
# Enable debug logging temporarily
# In modsecurity.conf
SecDebugLog /var/log/apache2/modsec_debug.log
SecDebugLogLevel 3 # Don't use 9 in production!
# Check which rules are slow
grep "Recipe:" /var/log/apache2/modsec_debug.log | \
awk '{print $NF}' | sort | uniq -c | sort -rn
Solutions:
- Disable expensive regex rules if not needed
- Use exclusion rules to skip inspection for static content
- Implement caching (Varnish) in front of Apache
- Consider upgrading hardware or implementing load balancing
Advanced: Integration and Monitoring
Integrating with SIEM
Export ModSecurity logs to centralized logging:
# Configure rsyslog to forward ModSecurity logs
sudo nano /etc/rsyslog.d/30-modsecurity.conf
# ModSecurity audit log forwarding
$ModLoad imfile
$InputFileName /var/log/apache2/modsec_audit.log
$InputFileTag modsec-audit:
$InputFileStateFile modsec-audit-state
$InputFileSeverity info
$InputFileFacility local7
$InputRunFileMonitor
# Forward to SIEM
local7.* @@siem.yourdomain.com:514
Restart rsyslog:
sudo systemctl restart rsyslog
Real-Time Alerting with fail2ban
Create custom fail2ban filter for ModSecurity:
sudo nano /etc/fail2ban/filter.d/modsecurity.conf
[Definition]
failregex = \[id ".*?"\] \[msg ".*?"\] \[severity "CRITICAL"\] \[hostname ".*?"\] \[uri ".*?"\] \[client .*?:<HOST>:\d+\]
ignoreregex =
Configure jail:
sudo nano /etc/fail2ban/jail.local
[modsecurity]
enabled = true
port = http,https
filter = modsecurity
logpath = /var/log/apache2/modsec_audit.log
maxretry = 5
bantime = 3600
findtime = 300
action = iptables-multiport[name=ModSecurity, port="http,https"]
Restart fail2ban:
sudo systemctl restart fail2ban
sudo fail2ban-client status modsecurity
Conclusion
You’ve now implemented a production-grade Web Application Firewall using Apache HTTPS, ModSecurity 2.9, and the OWASP Core Rule Set. This powerful combination provides comprehensive protection against the OWASP Top 10 vulnerabilities and many other attack vectors.
Key takeaways:
- Layered security: ModSecurity works best as part of a defense-in-depth strategy alongside secure coding, regular updates, and network firewalls
- Tuning is essential: Never deploy ModSecurity in blocking mode without adequate tuning; expect to spend 2-4 weeks in DetectionOnly mode initially
- Performance matters: Properly configured, ModSecurity adds only 5-15% overhead while providing enterprise-grade security
- Stay updated: OWASP CRS is actively maintained; schedule quarterly reviews and updates
- Monitor continuously: False positives will emerge as your application evolves; maintain ongoing monitoring and adjustment
Next steps:
- Join the OWASP ModSecurity Core Rule Set mailing list for updates
- Explore ModSecurity 3.x (libmodsecurity) for next-generation features
- Implement automated testing in your CI/CD pipeline to catch rule conflicts early
- Consider managed WAF services if you need 24/7 security operations
- Integrate ModSecurity logs with your SIEM for comprehensive security monitoring
ModSecurity has protected millions of web applications worldwide. With proper configuration and ongoing maintenance, it will significantly enhance your security posture while maintaining excellent performance.
References:
- OWASP ModSecurity Core Rule Set Project - Official CRS documentation and releases
- ModSecurity Reference Manual - Comprehensive directive and configuration guide
- Apache Module mod_ssl Documentation - Official Apache SSL/TLS configuration reference
- OWASP Top Ten - Understanding the vulnerabilities ModSecurity protects against
- ModSecurity GitHub Repository - Source code, issues, and community discussions