Nginx ModSecurity
Introduction
ModSecurity is an open-source Web Application Firewall (WAF) that can be integrated with Nginx to provide real-time protection against various web application attacks. It acts as a shield between your web application and potential attackers, inspecting incoming HTTP requests and outgoing responses to identify and block malicious traffic.
In this guide, we'll explore how to implement ModSecurity with Nginx, configure security rules, and protect your web applications from common security threats such as SQL injection, cross-site scripting (XSS), and more.
Why Use ModSecurity with Nginx?
- Layer of Defense: Adds an essential security layer to detect and prevent attacks
- Customizable Rules: Allows you to create and tailor rules specific to your application
- Real-time Monitoring: Provides visibility into attack patterns and security events
- OWASP Protection: Includes rules to protect against OWASP Top 10 vulnerabilities
- Minimal Performance Impact: When properly configured, adds minimal overhead
Prerequisites
Before we begin, ensure you have:
- A server with Nginx installed
- Root or sudo access to your server
- Basic knowledge of Nginx configuration
- Familiarity with the Linux command line
Installation
Step 1: Install Required Dependencies
First, we need to install the necessary dependencies to compile ModSecurity:
# For Debian/Ubuntu systems
sudo apt update
sudo apt install -y git build-essential libpcre3-dev libxml2-dev libyajl-dev \
libcurl4-openssl-dev libgeoip-dev liblmdb-dev libpcre++-dev pkgconf zlib1g-dev libssl-dev
Step 2: Clone and Compile ModSecurity
Next, we'll clone and compile the ModSecurity library:
# Create a working directory
mkdir ~/nginx-modsecurity
cd ~/nginx-modsecurity
# Clone ModSecurity repository
git clone --depth 1 -b v3/master --single-branch https://github.com/SpiderLabs/ModSecurity
cd ModSecurity
# Initialize and update submodules
git submodule init
git submodule update
# Build ModSecurity
./build.sh
./configure
make
sudo make install
Step 3: Download the Nginx Connector for ModSecurity
Now, we'll download the Nginx connector for ModSecurity:
cd ~/nginx-modsecurity
git clone --depth 1 https://github.com/SpiderLabs/ModSecurity-nginx.git
Step 4: Compile Nginx with ModSecurity
If you have Nginx installed from source, you'll need to recompile it with the ModSecurity module. If you installed Nginx from a package manager, you'll need to compile a dynamic module:
# Get Nginx version
NGINX_VERSION=$(nginx -v 2>&1 | awk -F/ '{print $2}')
# Download the corresponding Nginx source
wget https://nginx.org/download/nginx-$NGINX_VERSION.tar.gz
tar zxvf nginx-$NGINX_VERSION.tar.gz
cd nginx-$NGINX_VERSION
# Configure Nginx with ModSecurity module
./configure --with-compat --add-dynamic-module=../ModSecurity-nginx
# Compile only the modules
make modules
# Copy the module to Nginx modules directory
sudo cp objs/ngx_http_modsecurity_module.so /etc/nginx/modules/
Step 5: Configure Nginx to Use ModSecurity
Edit your Nginx configuration to load and use ModSecurity:
# In /etc/nginx/nginx.conf, at the top of the file
load_module modules/ngx_http_modsecurity_module.so;
http {
# Other http configurations...
# ModSecurity configuration
modsecurity on;
modsecurity_rules_file /etc/nginx/modsec/main.conf;
}
Step 6: Set Up ModSecurity Configuration
Create the basic ModSecurity configuration:
# Create the directory
sudo mkdir -p /etc/nginx/modsec
# Copy the recommended configuration
sudo cp ~/nginx-modsecurity/ModSecurity/modsecurity.conf-recommended /etc/nginx/modsec/modsecurity.conf
# Create the main configuration file
sudo touch /etc/nginx/modsec/main.conf
Edit the main configuration file /etc/nginx/modsec/main.conf
:
# Include the ModSecurity configuration
Include /etc/nginx/modsec/modsecurity.conf
# Set the default rules engine mode to On (actively blocking)
SecRuleEngine On
# Include OWASP Core Rule Set (CRS)
Include /etc/nginx/modsec/owasp-crs/crs-setup.conf
Include /etc/nginx/modsec/owasp-crs/rules/*.conf
Step 7: Install OWASP Core Rule Set (CRS)
The OWASP Core Rule Set provides a set of rules to protect against common web attacks:
cd /etc/nginx/modsec
sudo git clone https://github.com/coreruleset/coreruleset owasp-crs
sudo cp owasp-crs/crs-setup.conf.example owasp-crs/crs-setup.conf
Step 8: Configure ModSecurity Parameters
Edit the ModSecurity config file at /etc/nginx/modsec/modsecurity.conf
:
# -- Rule engine initialization ----------------------------------------------
# Enable ModSecurity
SecRuleEngine On
# -- Request body handling ---------------------------------------------------
# Allow ModSecurity to access request bodies
SecRequestBodyAccess On
# -- Response body handling --------------------------------------------------
# Allow ModSecurity to access response bodies
SecResponseBodyAccess On
# -- Audit logging ----------------------------------------------------------
# Log everything to a file
SecAuditEngine RelevantOnly
SecAuditLogRelevantStatus "^(?:5|4(?!04))"
SecAuditLogParts ABIJDEFHZ
SecAuditLogType Serial
SecAuditLog /var/log/nginx/modsec_audit.log
# -- Debug Mode --------------------------------------------------------------
# Disable debug mode in production
SecDebugLog /var/log/nginx/modsec_debug.log
SecDebugLogLevel 0
Step 9: Test and Restart Nginx
Test your Nginx configuration and restart the service:
sudo nginx -t
sudo systemctl restart nginx
Basic ModSecurity Rules
ModSecurity uses rules to detect and block malicious requests. Let's examine some basic rules:
Creating a Custom Rule File
Create a file for your custom rules:
sudo touch /etc/nginx/modsec/custom-rules.conf
Then include this file in your main configuration (/etc/nginx/modsec/main.conf
):
# Include custom rules
Include /etc/nginx/modsec/custom-rules.conf
Rule Structure
ModSecurity rules follow this structure:
SecRule VARIABLES "OPERATOR" "ACTIONS"
Where:
VARIABLES
: What to inspect (like ARGS, REQUEST_HEADERS, etc.)OPERATOR
: How to inspect (like regex matching with @rx)ACTIONS
: What to do when the rule matches (like block, log, etc.)
Example Rules
Here are some example rules to add to your custom-rules.conf
file:
Blocking SQL Injection Attempts
# Block SQL injection attempts
SecRule ARGS "@rx select.*from" \
"id:1000,\
phase:2,\
deny,\
status:403,\
log,\
msg:'Potential SQL Injection Attack'"
Blocking Cross-Site Scripting (XSS) Attempts
# Block XSS attempts
SecRule ARGS "@rx <script.*>" \
"id:1001,\
phase:2,\
deny,\
status:403,\
log,\
msg:'Potential XSS Attack'"
Rate Limiting Rules
# Rate limiting - track by IP
SecAction \
"id:1002,\
phase:1,\
nolog,\
pass,\
initcol:ip=%{REMOTE_ADDR},\
setvar:ip.requests=+1,\
expirevar:ip.requests=60"
# Block if too many requests
SecRule IP:REQUESTS "@gt 100" \
"id:1003,\
phase:1,\
deny,\
status:429,\
log,\
msg:'Rate limit exceeded'"
Real-World Example: Securing a WordPress Site
Let's see a practical example of securing a WordPress site with ModSecurity and Nginx:
server {
listen 80;
server_name example.com;
# ModSecurity configuration
modsecurity on;
modsecurity_rules_file /etc/nginx/modsec/main.conf;
# Specific WordPress protection rules
location /wp-login.php {
# Apply additional ModSecurity rules to WordPress login
modsecurity_rules '
# Limit login attempts
SecRule IP:WORDPRESS_LOGIN "@gt 5" \
"id:2000,\
phase:1,\
deny,\
status:403,\
log,\
setvar:ip.wordpress_block=1,\
expirevar:ip.wordpress_block=300,\
msg:\'WordPress login attempt limit exceeded\'"
';
# Regular location configuration
try_files $uri $uri/ /index.php?$args;
# Pass to PHP handler
include fastcgi_params;
fastcgi_pass unix:/var/run/php/php7.4-fpm.sock;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
# Protect wp-config.php
location = /wp-config.php {
deny all;
}
# Other WordPress locations
location / {
try_files $uri $uri/ /index.php?$args;
# Pass to PHP handler
location ~ \.php$ {
include fastcgi_params;
fastcgi_pass unix:/var/run/php/php7.4-fpm.sock;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
}
}
In this example, we've applied ModSecurity to our WordPress site with special protection for the login page.
Advanced Configuration
Adjusting Detection Sensitivity
ModSecurity can be too restrictive by default. You can adjust its sensitivity by:
-
Using the
SecRuleEngine
directive:SecRuleEngine On
: Full blocking modeSecRuleEngine DetectionOnly
: Detection without blockingSecRuleEngine Off
: Disabled
-
Selectively disabling rules:
# Disable a specific rule
SecRuleRemoveById 942100
# Disable a group of rules
SecRuleRemoveByTag "attack-sqli"
Logging and Monitoring
To effectively use ModSecurity, set up proper logging:
# In modsecurity.conf
SecAuditLog /var/log/nginx/modsec_audit.log
SecAuditLogFormat JSON
You can then monitor these logs for attack patterns using tools like:
- ELK Stack (Elasticsearch, Logstash, Kibana)
- Graylog
- Splunk
Performance Optimization
To maintain performance with ModSecurity:
- Limit which parts of the request to inspect:
# Only inspect specific file types
SecRule REQUEST_FILENAME "!\.(gif|jpg|png|css|js)$" \
"id:1004,\
phase:1,\
pass,\
nolog,\
ctl:ruleEngine=On"
SecRule REQUEST_FILENAME "\.(gif|jpg|png|css|js)$" \
"id:1005,\
phase:1,\
pass,\
nolog,\
ctl:ruleEngine=Off"
- Adjust inspection limits:
# Adjust request body limit
SecRequestBodyLimit 13107200
SecRequestBodyNoFilesLimit 131072
Troubleshooting
Common Issues and Solutions
-
False Positives: ModSecurity might block legitimate traffic
Solution: Use DetectionOnly mode initially and analyze logs:
SecRuleEngine DetectionOnly
-
Performance Issues: High CPU usage or latency
Solution: Optimize rules and increase resource limits:
# Adjust timeouts
SecRequestBodyTimeout 60
# Disable inspection for static content
SecRule REQUEST_FILENAME "\.(jpg|jpeg|png|gif|css|js)$" "id:1006,phase:1,allow,nolog" -
Rule Conflicts: Multiple rules causing unexpected behavior
Solution: Debug by identifying problematic rules:
# Increase debug level temporarily
SecDebugLogLevel 3
How to Debug ModSecurity Issues
-
Enable debug logging with an appropriate level:
SecDebugLog /var/log/nginx/modsec_debug.log
SecDebugLogLevel 3 -
Test specific requests that are causing issues:
bashcurl -v "http://your-website.com/?test=select%20from"
-
Check the debug log to see which rules triggered:
bashtail -f /var/log/nginx/modsec_debug.log
Summary
ModSecurity provides a powerful layer of protection for your Nginx web server. In this guide, we've covered:
- Installing and configuring ModSecurity with Nginx
- Setting up basic and custom security rules
- Applying ModSecurity to protect real-world applications like WordPress
- Advanced configuration options for performance and usability
- Troubleshooting common issues
By implementing ModSecurity, you can significantly enhance your web application's security posture and protect against common attacks. Remember that security is an ongoing process that requires regular updates and monitoring to stay effective against evolving threats.
Additional Resources
For further learning:
- Official ModSecurity Documentation
- OWASP ModSecurity Core Rule Set (CRS) Documentation
- ModSecurity Handbook by Ivan Ristić
Exercises
- Install ModSecurity with Nginx on a test server and verify it's working.
- Create a custom rule to block requests containing a specific User-Agent.
- Implement ModSecurity in DetectionOnly mode and analyze logs for potential attacks.
- Set up protection for a specific web application with custom rules.
- Experiment with different SecRuleEngine settings and observe the effects.
If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)