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

Unlock Full Tutorial

This chapter is paid content. Join the project to unlock over 5000 words of deep analysis, including 10+ god-tier Prompts and real Source Code examples!