ModSecurity WAF: Protecting Web Applications Against Common Attacks
Web attacks have never been more sophisticated. SQL injection, XSS (Cross-Site Scripting), command injection, DDoS... These vulnerabilities still dominate the OWASP Top 10 after two decades. Why? Because they work.
The good news: you don't need to wait for your development team to audit every line of code. ModSecurity, an open-source Web Application Firewall (WAF), can detect and block these attacks before they reach your application.
This article covers:
- What ModSecurity actually does (and what it doesn't)
- How to implement it on Kubernetes with Ingress NGINX
- Practical configuration of the Core Rule Set (CRS)
- Deployment best practices
What is ModSecurity?
ModSecurity is an open-source WAF originally designed for Nginx and Apache. It functions as an application-level filter that:
- Inspects every incoming HTTP request
- Applies security rules (Core Rule Set - CRS)
- Blocks or logs suspicious requests
- Records security events for audit trails
Unlike a network firewall (Layer 3/4), ModSecurity understands HTTP/HTTPS and can analyze application-level threats.
What ModSecurity protects against
ModSecurity protects against most OWASP Top 10 attacks:
| Attack | Detection |
|---|---|
| XSS (Cross-Site Scripting) | Detects injected scripts, <script> tags, event handlers |
| SQL Injection | Recognizes SQL patterns (' OR '1'='1', UNION SELECT) |
| Command Injection | Blocks shell command execution attempts (; rm -rf /) |
| Path Traversal | Prevents access to parent directories (../../../etc/passwd) |
| Clickjacking | Validates response headers (X-Frame-Options) |
| Buffer Overflow | Limits upload size and header lengths |
| Application-Layer DDoS | Rate limiting and anomaly detection |
What ModSecurity cannot do
Be realistic:
- Doesn't replace input validation: your code must still validate inputs
- Can't fix business logic flaws: if your logic is broken, ModSecurity can't fix it
- Doesn't prevent weak authentication: ModSecurity doesn't validate passwords
- Isn't complete DDoS protection: protects against application attacks, not raw network floods
ModSecurity is a defense layer, not a complete solution.
ModSecurity on Kubernetes: Ingress NGINX + ModSecurity
On Kubernetes, you likely deploy applications behind an Ingress Controller. Ingress NGINX (the most popular Nginx controller) has native ModSecurity support.
Typical architecture on Hikube
Internet
|
v
Ingress NGINX (with ModSecurity WAF)
|
+-- Service API (port 8080)
+-- Service Web (port 3000)
+-- Service Admin (port 8888)
Every request passes through Ingress NGINX. ModSecurity inspects them before they touch your application.
Deployment on Hikube
If you're using Hikube (Hidora's managed Kubernetes platform), you can enable ModSecurity directly. Here's how:
1. Install Ingress NGINX with ModSecurity
If your cluster doesn't have Ingress NGINX with ModSecurity, install it via Helm:
helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
helm repo update
helm install ingress-nginx ingress-nginx/ingress-nginx \
--namespace ingress-nginx \
--create-namespace \
--set controller.image.image=ingress-nginx/controller \
--set controller.metrics.enabled=true
ModSecurity is often pre-installed. Verify:
kubectl exec -it -n ingress-nginx \
pod/ingress-nginx-controller-xxx -- \
/usr/local/modsecurity/bin/modsec-rules-check || \
echo "ModSecurity not installed"
2. Configure ModSecurity in the Ingress
Once Ingress NGINX is deployed, enable ModSecurity via annotations:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: myapp-ingress
annotations:
# Enable ModSecurity
nginx.ingress.kubernetes.io/enable-modsecurity: "true"
# Use OWASP Core Rule Set
nginx.ingress.kubernetes.io/enable-owasp-core-rules: "true"
# Mode: "Off" (disabled) | "DetectionOnly" (log only) | "On" (blocks)
nginx.ingress.kubernetes.io/modsecurity-mode: "DetectionOnly"
# Point to config file
nginx.ingress.kubernetes.io/modsecurity-conf: |
SecRuleEngine On
SecAuditLogRelevantStatus "^(?:5|4(?!04))"
SecAuditLogParts ABCIJDEFHZ
SecAuditLogFormat JSON
spec:
rules:
- host: api.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: myapp-api
port:
number: 8080
3. Configure ModSecurity rules (Core Rule Set)
ModSecurity relies on rules. The best approach is to use the official OWASP Core Rule Set (CRS).
Create a ConfigMap with rules:
apiVersion: v1
kind: ConfigMap
metadata:
name: modsecurity-rules
namespace: ingress-nginx
data:
modsecurity.conf: |
# Base configuration
SecRuleEngine On
SecRequestBodyAccess On
SecRequestBodyLimit 10485760
SecRequestBodyNoFiles Off
SecRequestBodyLimitAction ProcessPartial
SecResponseBodyAccess Off
SecResponseBodyMimeType text/plain text/html text/xml
SecResponseBodyMimeTypesClear
SecPcreMatchLimit 1000
SecPcreMatchLimitRecursion 1000
# Audit logging
SecAuditEngine RelevantOnly
SecAuditLogRelevantStatus "^(?:5|4(?!04))"
SecAuditLogParts ABCIJDEFHZ
SecAuditLogFormat JSON
SecAuditLogType Serial
SecAuditLog /var/log/modsecurity/audit.log
# OWASP Core Rule Set
IncludeOptional /etc/modsecurity/crs/crs-setup.conf
IncludeOptional /etc/modsecurity/crs/rules/*.conf
# Custom rules
custom-rules.conf: |
# Block vulnerability scanners (sqlmap, nikto, etc.)
SecRule REQUEST_HEADERS:User-Agent "@contains sqlmap" "id:1001,phase:1,log,block,msg:'SQLMap scan attempt'"
SecRule REQUEST_HEADERS:User-Agent "@contains nikto" "id:1002,phase:1,log,block,msg:'Nikto scanner'"
# Limit file uploads
SecRule FILES_TMPNAMES "!@inspectFile /path/to/whitelist.txt" \
"id:1003,phase:2,deny,status:403,msg:'File upload blocked'"
# Simple rate limiting (10 requests/sec per IP)
SecRule IP:excessive_requests "@gt 10" \
"id:1004,phase:1,deny,status:429,msg:'Too many requests'"
4. Test with DetectionOnly first
Never deploy ModSecurity directly in "On" (blocking) mode to production.
Start with DetectionOnly to observe false positives:
# Watch for blocked requests
kubectl logs -n ingress-nginx -l app.kubernetes.io/name=ingress-nginx -f | grep modsecurity
After 1-2 weeks of observation, switch to On.
5. Exclude false positives
Inevitably, some legitimate requests will be flagged. Create exclusions:
# Exclude paths from ModSecurity
apiVersion: v1
kind: ConfigMap
metadata:
name: modsecurity-exclusions
namespace: ingress-nginx
data:
exclusions.conf: |
# Exclude healthchecks
SecRule REQUEST_URI "@startsWith /health" "id:2001,phase:1,nolog,pass"
# Exclude legitimate uploads (e.g., CSV files)
SecRule REQUEST_URI "@startsWith /api/import" \
"id:2002,phase:2,nolog,pass,msg:'Allow CSV import'"
# Exclude POST with valid JSON to API
SecRule REQUEST_METHOD "POST" \
"id:2003,phase:1,nolog,pass,msg:'Allow POST to API'"
Practical implementation: Complete example
Here's a complete example for a web application on Hikube:
---
# 1. ConfigMap for ModSecurity rules
apiVersion: v1
kind: ConfigMap
metadata:
name: modsecurity-rules
namespace: default
data:
modsecurity.conf: |
SecRuleEngine On
SecRequestBodyAccess On
SecAuditEngine RelevantOnly
SecAuditLogRelevantStatus "^(?:5|4(?!04))"
IncludeOptional /etc/modsecurity/crs/crs-setup.conf
IncludeOptional /etc/modsecurity/crs/rules/*.conf
---
# 2. Ingress with ModSecurity enabled
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: ecommerce-ingress
annotations:
nginx.ingress.kubernetes.io/enable-modsecurity: "true"
nginx.ingress.kubernetes.io/enable-owasp-core-rules: "true"
nginx.ingress.kubernetes.io/modsecurity-mode: "DetectionOnly" # Change to "On" after testing
spec:
ingressClassName: nginx
rules:
- host: shop.example.ch
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: ecommerce-app
port:
number: 3000
- path: /api
pathType: Prefix
backend:
service:
name: ecommerce-api
port:
number: 8080
tls:
- hosts:
- shop.example.ch
secretName: tls-cert
---
# 3. Service
apiVersion: v1
kind: Service
metadata:
name: ecommerce-app
spec:
selector:
app: ecommerce
ports:
- port: 3000
targetPort: 3000
---
# 4. Example Pod
apiVersion: apps/v1
kind: Deployment
metadata:
name: ecommerce
spec:
replicas: 2
selector:
matchLabels:
app: ecommerce
template:
metadata:
labels:
app: ecommerce
spec:
containers:
- name: app
image: myregistry.azurecr.io/ecommerce:v1.0
ports:
- containerPort: 3000
resources:
requests:
cpu: 100m
memory: 256Mi
limits:
cpu: 500m
memory: 512Mi
securityContext:
runAsNonRoot: true
readOnlyRootFilesystem: true
Monitoring and alerting
ModSecurity generates logs. You must monitor them:
1. Local logs in Ingress NGINX controller
# Access the Ingress NGINX pod
kubectl exec -it -n ingress-nginx \
pod/ingress-nginx-controller-xxx -- bash
# Check logs
tail -f /var/log/modsecurity/audit.log | jq .
2. Centralize logs (ELK, Splunk, Datadog)
Configure Ingress NGINX to send ModSecurity logs to your centralized system:
apiVersion: v1
kind: ConfigMap
metadata:
name: ingress-nginx-config
namespace: ingress-nginx
data:
modsecurity-log-format: |
{"timestamp": "$time_iso8601", "client": "$remote_addr", "request": "$request",
"status": "$status", "modsecurity_action": "$modsecurity_action"}
3. Alert on attacks
Configure Prometheus/Alertmanager alerts:
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
name: modsecurity-alerts
spec:
groups:
- name: modsecurity
rules:
- alert: HighModSecurityBlockRate
expr: rate(nginx_modsecurity_blocked_total[5m]) > 10
for: 5m
annotations:
summary: "High rate of requests blocked by ModSecurity"
Deployment best practices
1. Start in DetectionOnly mode
Never block without observing first. False positives are inevitable.
nginx.ingress.kubernetes.io/modsecurity-mode: "DetectionOnly"
Observe for 1-2 weeks, then switch to On.
2. Implement exclusion whitelist
Some paths should not be filtered (healthchecks, webhooks, etc.):
SecRule REQUEST_URI "@startsWith /health" "id:100,phase:1,pass,nolog"
SecRule REQUEST_URI "@eq /webhook/payment" "id:101,phase:1,pass,nolog"
3. Structured JSON logs
Use JSON format for easier analysis:
SecAuditLogFormat JSON
4. Log rotation
ModSecurity logs can become large. Configure rotation:
SecAuditLogStorageDir /var/log/modsecurity
SecAuditEngine RelevantOnly # Log only attacks, not everything
5. Regularly test your rules
Verify your rules still work:
# Test with XSS
curl "https://api.example.ch/search?q=<script>alert(1)</script>"
# Should be blocked or logged
# Test with SQL injection
curl "https://api.example.ch/users?id=1' OR '1'='1"
# Should be blocked or logged
Integration with Hidora managed services
At Hidora, we offer two levels of WAF security:
1. Standard WAF (included in Hikube)
- ModSecurity with Core Rule Set
- Automatic HTTPS/TLS
- Basic Layer 4 DDoS protection
- Centralized logging
2. Advanced WAF (via Managed Services)
- Machine learning for anomalies
- Application-specific custom rules
- 24/7 monitoring and incident response
- Swiss compliance (LPD) and ISO 27001
See our Managed Services page for more information.
Conclusion
ModSecurity isn't a silver bullet, but it's an extremely effective tool for stopping common attacks. Combined with Kubernetes and Ingress NGINX, it provides application-level security without code modifications.
Implementation summary:
- Deploy Ingress NGINX with ModSecurity on Hikube
- Enable OWASP Core Rule Set rules
- Start in
DetectionOnlymode - Observe and exclude false positives
- Switch to
Onmode after validation - Centralize logs and configure alerts
It's a minimal investment for massive protection against web attacks.
Related reading:
Found this helpful? Discover how Hidora can secure your web applications: Hikube Platform · Managed Services · Professional Services



