Building a Production WAF with Apache HTTPS, ModSecurity, and OWASP CRS

19 min read
modsecurity apache web-security owasp waf 2024

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.

HTTPS Request

SSL/TLS Layer

Request Inspection

Match Found

No Match

Response

Response Inspection

403 Forbidden

Clean Response

Client Browser

Apache Web Server

ModSecurity Engine

OWASP CRS Rules

Log & Block/Detect

Pass to Application

The flow explained:

  1. Client request arrives via HTTPS at Apache
  2. Apache SSL/TLS layer decrypts the connection
  3. ModSecurity engine intercepts the request before it reaches your application
  4. OWASP CRS rules analyze the request content for attack patterns
  5. Decision point: If malicious patterns are detected, the request is blocked (403 error); otherwise, it passes through
  6. Response inspection: ModSecurity can also examine outbound traffic for data leakage
  7. 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:

  1. Start with DetectionOnly mode and high anomaly thresholds
  2. Collect baseline data from legitimate traffic (1-2 weeks)
  3. 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:

  1. Layered security: ModSecurity works best as part of a defense-in-depth strategy alongside secure coding, regular updates, and network firewalls
  2. Tuning is essential: Never deploy ModSecurity in blocking mode without adequate tuning; expect to spend 2-4 weeks in DetectionOnly mode initially
  3. Performance matters: Properly configured, ModSecurity adds only 5-15% overhead while providing enterprise-grade security
  4. Stay updated: OWASP CRS is actively maintained; schedule quarterly reviews and updates
  5. 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:

  1. OWASP ModSecurity Core Rule Set Project - Official CRS documentation and releases
  2. ModSecurity Reference Manual - Comprehensive directive and configuration guide
  3. Apache Module mod_ssl Documentation - Official Apache SSL/TLS configuration reference
  4. OWASP Top Ten - Understanding the vulnerabilities ModSecurity protects against
  5. ModSecurity GitHub Repository - Source code, issues, and community discussions