Security
Blog
Security12 min

ModSecurity WAF: Protecting Web Applications Against Common Attacks

Mattia Eleuteri29 avril 2022

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:

  1. What ModSecurity actually does (and what it doesn't)
  2. How to implement it on Kubernetes with Ingress NGINX
  3. Practical configuration of the Core Rule Set (CRS)
  4. 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:

  1. Deploy Ingress NGINX with ModSecurity on Hikube
  2. Enable OWASP Core Rule Set rules
  3. Start in DetectionOnly mode
  4. Observe and exclude false positives
  5. Switch to On mode after validation
  6. 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

Does this article resonate?

Hidora can support you on this topic.

Need support?

Let's talk about your project. 30 minutes, no strings attached.