Web Application Firewall (WAF) Deep Dive
Vibe Prompt
"Generate an OWASP ModSecurity Core Rule Set (CRS) configuration file to protect against SQL Injection and Cross-Site Scripting (XSS) attacks, optimized for a production Nginx reverse proxy environment."
ModSecurity Installation & Deployment Architecture
Option 1: Docker-Based Deployment (Fastest Path to Production)
This approach isolates the WAF layer, making it portable across environments (local dev, CI/CD, Kubernetes, VMs).
# Pull the official OWASP ModSecurity CRS image hardened for Nginx
docker pull owasp/modsecurity-crs:nginx
# Run with persistent configuration volume
# Maps host port 80/443 to container ports
# Mounts local modsec.conf for custom tuning (paranoia levels, exclusions)
docker run -d \
--name waf-gateway \
-p 80:80 \
-p 443:443 \
-v $(pwd)/modsec.conf:/etc/nginx/modsec/modsec.conf:ro \
-v $(pwd)/crs-exclusions.conf:/etc/modsecurity.d/owasp-crs/rules/REQUEST-900-EXCLUSION-RULES-BEFORE-CRS.conf:ro \
--restart unless-stopped \
owasp/modsecurity-crs:nginx
Option 2: Native Nginx Compilation (Maximum Performance/Control)
For high-throughput environments where the WAF runs on the same host as the application server (sidecar pattern) or a dedicated edge node.
# Ubuntu/Debian Installation
apt-get update && apt-get install -y \
libmodsecurity3 \
libmodsecurity-dev \
nginx-module-modsecurity
# Verify dynamic module loading in /etc/nginx/nginx.conf
# load_module modules/ngx_http_modsecurity_module.so;
OWASP Core Rule Set (CRS) Configuration Deep Dive
The crs-setup.conf โ The Control Center
This file must be customized before enabling rules. It defines the "Paranoia Level" (PL) which dictates aggressiveness.
# /etc/modsecurity.d/owasp-crs/crs-setup.conf
# ---------------------------------------------------------
# PARANOIA LEVEL (PL) SELECTION
# ---------------------------------------------------------
# PL 1: Default. Low false positives. Catches obvious attacks.
# PL 2: Recommended for Production. Catches obfuscated attacks.
# PL 3: High Security. Expect false positives; requires tuning.
# PL 4: Paranoid. Blocks almost anything suspicious. API breaking likely.
# ---------------------------------------------------------
SecAction \
"id:900000,\
phase:1,\
nolog,\
pass,\
t:none,\
setvar:tx.paranoia_level=2"
# ---------------------------------------------------------
# ANOMALY SCORING MODE (Default in CRS v3+)
# ---------------------------------------------------------
# Instead of blocking immediately on first match, rules increment
# 'tx.anomaly_score'. Blocking happens at the end (Rule 949110)
# if score exceeds 'tx.inbound_anomaly_score_threshold' (default 5).
# This allows legitimate requests with minor anomalies to pass.
# ---------------------------------------------------------
SecAction \
"id:900010,\
phase:1,\
nolog,\
pass,\
t:none,\
setvar:tx.inbound_anomaly_score_threshold=5,\
setvar:tx.outbound_anomaly_score_threshold=4"
# ---------------------------------------------------------
# BLOCKING BEHAVIOR
# ---------------------------------------------------------
# Set to 'deny' (403) or 'drop' (TCP RST) or 'redirect'
SecAction "id:900020, phase:1, nolog, pass, t:none, setvar:tx.blocking_mode=deny"
# ---------------------------------------------------------
# EXCLUSIONS (Critical for reducing False Positives)
# ---------------------------------------------------------
# Example: Disable SQLi detection (942100) for 'search_query' argument
# SecRule REQUEST_URI "@beginsWith /api/search" \
# "id:1000001,phase:1,pass,nolog,ctl:ruleRemoveById=942100"
Core Rule Inclusion Order (Critical for Logic Flow)
Order matters: Setup -> Exclusions (Before) -> Rules -> Exclusions (After) -> Anomaly Check.
# /etc/nginx/modsec/main.conf (or included in http/server block)
# 1. Load ModSecurity Engine
modsecurity on;
modsecurity_rules_file /etc/nginx/modsec/includes.conf;
# --- File: /etc/nginx/modsec/includes.conf ---
# 1. CRS Setup (Defines variables: PL, Thresholds)
Include /etc/modsecurity.d/owasp-crs/crs-setup.conf
# 2. Custom Exclusions BEFORE CRS (False Positive Tuning)
Include /etc/modsecurity.d/owasp-crs/rules/REQUEST-900-EXCLUSION-RULES-BEFORE-CRS.conf
# 3. CRS Rule Files (Executed in numerical order)
# 910: Protocol Enforcement (HTTP RFC compliance)
Include /etc/modsecurity.d/owasp-crs/rules/REQUEST-910-IP-REPUTATION.conf
Include /etc/modsecurity.d/owasp-crs/rules/REQUEST-911-METHOD-ENFORCEMENT.conf
Include /etc/modsecurity.d/owasp-crs/rules/REQUEST-912-DOS-PROTECTION.conf
Include /etc/modsecurity.d/owasp-crs/rules/REQUEST-913-SCANNER-DETECTION.conf
# 920: Protocol Attacks (Invalid encoding, null bytes)
Include /etc/modsecurity.d/owasp-crs/rules/REQUEST-920-PROTOCOL-ENFORCEMENT.conf
Include /etc/modsecurity.d/owasp-crs/rules/REQUEST-921-PROTOCOL-ATTACK.conf
# 930: Application Attacks - LFI/RFI
Include /etc/modsecurity.d/owasp-crs/rules/REQUEST-930-APPLICATION-ATTACK-LFI.conf
Include /etc/modsecurity.d/owasp-crs/rules/REQUEST-931-APPLICATION-ATTACK-RFI.conf
Include /etc/modsecurity.d/owasp-crs/rules/REQUEST-932-APPLICATION-ATTACK-RCE.conf
Include /etc/modsecurity.d/owasp-crs/rules/REQUEST-933-APPLICATION-ATTACK-PHP.conf
Include /etc/modsecurity.d/owasp-crs/rules/REQUEST-934-APPLICATION-ATTACK-NODEJS.conf
# 941: XSS (Cross-Site Scripting)
Include /etc/modsecurity.d/owasp-crs/rules/REQUEST-941-APPLICATION-ATTACK-XSS.conf
# 942: SQL Injection (SQLi)
Include /etc/modsecurity.d/owasp-crs/rules/REQUEST-942-APPLICATION-ATTACK-SQLI.conf
# 943: Command Injection / Shell Shock
Include /etc/modsecurity.d/owasp-crs/rules/REQUEST-943-APPLICATION-ATTACK-CMDI.conf
# 944: Generic/Other Injections (XXE, LDAP, SSI)
Include /etc/modsecurity.d/owasp-crs/rules/REQUEST-944-APPLICATION-ATTACK-GENERIC.conf
# 950: Data Leakages (Credit cards, SSN, PHP errors in response)
Include /etc/modsecurity.d/owasp-crs/rules/RESPONSE-950-DATA-LEAKAGES.conf
Include /etc/modsecurity.d/owasp-crs/rules/RESPONSE-951-DATA-LEAKAGES-SQL.conf
Include /etc/modsecurity.d/owasp-crs/rules/RESPONSE-952-DATA-LEAKAGES-JAVA.conf
Include /etc/modsecurity.d/owasp-crs/rules/RESPONSE-953-DATA-LEAKAGES-PHP.conf
Include /etc/modsecurity.d/owasp-crs/rules/RESPONSE-954-DATA-LEAKAGES-IIS.conf
# 959: Blocking Evaluation (The "Decision Engine")
# Checks tx.anomaly_score against threshold. Returns 403 if exceeded.
Include /etc/modsecurity.d/owasp-crs/rules/RESPONSE-959-BLOCKING-EVALUATION.conf
# 980: Correlation (Optional, requires logging backend)
Include /etc/modsecurity.d/owasp-crs/rules/RESPONSE-980-CORRELATION.conf
# 4. Custom Exclusions AFTER CRS (Response filtering, specific bypasses)
Include /etc/modsecurity.d/owasp-crs/rules/RESPONSE-999-EXCLUSION-RULES-AFTER-CRS.conf
Custom Rule Examples: SQLi & XSS Logic Explained
You rarely write raw SecRule @detectSQLi anymore; CRS v3 uses macro expansions and anomaly scoring. However, understanding the primitives helps debugging.
# LOW LEVEL PRIMITIVE (What CRS uses internally)
# @detectSQLi checks libinjection_sqli (tokenizer based)
# @detectXSS checks libinjection_xss (HTML5 parser based)
# EXAMPLE: Custom High-Confidence SQLi Rule (Immediate Block, No Anomaly Scoring)
# Use sparingly. Place in REQUEST-900-EXCLUSION-RULES-BEFORE-CRS.conf or custom file.
SecRule ARGS|ARGS_NAMES|REQUEST_HEADERS:User-Agent|REQUEST_HEADERS:Referer \
"@detectSQLi" \
"id:1000010,\
phase:2,\
block,\
t:none,t:lowercase,t:urlDecodeUni,t:htmlEntityDecode,t:jsDecode,t:cssDecode,\
msg:'Custom: High Confidence SQL Injection via libinjection',\
logdata:'Matched Data: %{TX.0}',\
severity:'CRITICAL',\
tag:'attack-sqli',\
tag:'custom-rule'"
# EXAMPLE: Virtual Patching a Specific CVE (e.g., CVE-2021-44228 Log4Shell)
# Inspect *all* headers and body for JNDI lookup pattern
SecRule REQUEST_HEADERS|REQUEST_BODY|ARGS_NAMES|ARGS \
"@rx \$\{jndi:(ldap|rmi|dns|iiop)://" \
"id:1000020,\
phase:2,\
block,\
t:none,t:lowercase,t:urlDecodeUni,\
msg:'CVE-2021-44228 Log4Shell JNDI Injection Attempt',\
severity:'CRITICAL',\
tag:'cve-2021-44228',\
tag:'attack-rce'"
Request Body Limits & Performance Tuning
Prevent DoS via large payloads and tune buffer sizes.
# /etc/nginx/modsec/modsec.conf (or main server block)
# Max request body size ModSecurity will buffer for inspection (1MB default)
# Increase for file upload endpoints (e.g., 100MB = 104857600)
SecRequestBodyLimit 10485760
SecRequestBodyNoFilesLimit 1048576
# In-memory buffer limit. If body > this, written to disk (slower).
SecRequestBodyInMemoryLimit 131072
# Response Body Buffering (Required for Data Leakage rules 950-954)
SecResponseBodyAccess On
SecResponseBodyMimeType text/plain text/html text/xml application/json
SecResponseBodyLimit 1048576
# Audit Logging Configuration (Critical for SOC/SIEM integration)
SecAuditEngine RelevantOnly # Log only transactions that trigger rules/warnings
SecAuditLogRelevantStatus "^(?:5|4(?:0[0-?!(04|03|01|00))))" # Log 4xx (except 400,401,403,404) and 5xx
SecAuditLogParts ABIJDEFHZ # A=Header, B=Request Headers, I=Request Body, J=Uploaded Files, D=Response Headers, E=Response Body, F=Files, H=Audit Trail, Z=Footer
SecAuditLogType Concurrent
SecAuditLog /var/log/modsec/audit.log
SecAuditLogStorageDir /var/log/modsec/data
# Debug Log (Disable in production!)
SecDebugLog /var/log/modsec/debug.log
SecDebugLogLevel 0
Cloudflare WAF: Managed Edge Protection
Why Cloudflare WAF? (The "Why" for Founders/Devs)
- Zero Operational Overhead: No servers to patch, no rule updates to apply manually.
- DDoS Absorption at Edge: Volumetric attacks (Layer 3/4/7) die at Cloudflare's 300+ Tbps network before hitting your origin bandwidth bill.
- Intelligence Feed: Threat intelligence from 20M+ properties updates rules in seconds (e.g., Log4Shell signatures deployed globally in < 3 mins).
- Cost Predictability: Fixed price (Pro/Business/Enterprise) vs. variable EC2/Azure egress costs during attacks.
Dashboard Configuration Strategy (Terraform/IaC Preferred)
Do not click in UI. Define as Code.
# terraform/cloudflare_waf.tf
resource "cloudflare_filter" "sqli_block" {
zone_id = var.zone_id
description = "Block SQL Injection Attempts (Managed + Custom)"
expression = "(cf.waf.managed_rulesets.owasp.rule_id[942100] or cf.waf.managed_rulesets.owasp.rule_id[942110] or cf.waf.managed_rulesets.owasp.rule_id[942120] or cf.waf.managed_rulesets.owasp.rule_id[942130] or cf.waf.managed_rulesets.owasp.rule_id[942140] or cf.waf.managed_rulesets.owasp.rule_id[942150] or cf.waf.managed_rulesets.owasp.rule_id[942160] or cf.waf.managed_rulesets.owasp.rule_id[942170] or cf.waf.managed_rulesets.owasp.rule_id[942180] or cf.waf.managed_rulesets.owasp.rule_id[942190] or cf.waf.managed_rulesets.owasp.rule_id[942200] or cf.waf.managed_rulesets.owasp.rule_id[942210] or cf.waf.managed_rulesets.owasp.rule_id[942220] or cf.waf.managed_rulesets.owasp.rule_id[942230] or cf.waf.managed_rulesets.owasp.rule_id[942240] or cf.waf.managed_rulesets.owasp.rule_id[942250] or cf.waf.managed_rulesets.owasp.rule_id[942260] or cf.waf.managed_rulesets.owasp.rule_id[942270] or cf.waf.managed_rulesets.owasp.rule_id[942280] or cf.waf.managed_rulesets.owasp.rule_id[942290] or cf.waf.managed_rulesets.owasp.rule_id[942300] or cf.waf.managed_rulesets.owasp.rule_id[942310] or cf.waf.managed_rulesets.owasp.rule_id[942320] or cf.waf.managed_rulesets.owasp.rule_id[942330] or cf.waf.managed_rulesets.owasp.rule_id[942340] or cf.waf.managed_rulesets.owasp.rule_id[942350] or cf.waf.managed_rulesets.owasp.rule_id[942360] or cf.waf.managed_rulesets.owasp.rule_id[942370] or cf.waf.managed_rulesets.owasp.rule_id[942380] or cf.waf.managed_rulesets.owasp.rule_id[942390] or cf.waf.managed_rulesets.owasp.rule_id[942400] or cf.waf.managed_rulesets.owasp.rule_id[942410] or cf.waf.managed_rulesets.owasp.rule_id[942420] or cf.waf.managed_rulesets.owasp.rule_id[942430] or cf.waf.managed_rulesets.owasp.rule_id[942440] or cf.waf.managed_rulesets.owasp.rule_id[942450] or cf