Why We Rebuilt the WAF
Our original WAF had 15 handwritten detection rules. It caught obvious scanner noise, but as our platform grew we started seeing more sophisticated probes — double-encoded path traversal, Log4Shell variants, SSRF attempts targeting cloud metadata endpoints, and bots that spread their requests over minutes to avoid rate limits.
In March 2026 we rewrote the engine from scratch. The result is a 34-rule system covering 9 OWASP attack categories, with anomaly scoring, IP reputation tracking, and automated enforcement running every two minutes.
The 9 Detection Categories
Rules are now grouped into functional categories, each tuned to a specific threat family:
- Injection — SQL, NoSQL, LDAP, command injection, XXE, and server-side template injection (SSTI)
- XSS / Client-side — cross-site scripting payloads, prototype pollution, and open redirects
- File Inclusion — LFI / path traversal, SSRF to cloud metadata (169.254.169.254), and UTF-8 overlong encoding evasion
- Remote Code Execution — Log4Shell JNDI lookup, Shellshock, Spring4Shell, and popular CVEs (MOVEit, Citrix)
- Probes — PHP file probes, backup file enumeration, WordPress/Atlassian endpoint scanning
- Bot — scanner User-Agent strings, webshell and cryptominer agent signatures, empty User-Agent
- Protocol — HTTP request smuggling, header injection, CRLF injection, JWT attack patterns
- Rate — 404 storms and rapid 4xx bursts that indicate automated enumeration
- Honeypot — decoy paths that only automated attackers visit; single hit = 365-day ban
Multi-Layer URL Decoding
A common evasion technique is double- or triple-encoding a payload so a simple string match misses it. For example, %252F decodes to %2F which then decodes to /. Our previous WAF would pass this through untouched.
The new engine runs every request URL through a deepDecode(input, maxDepth=3) function before applying any rule. It iterates up to three decode cycles, stopping as soon as the output stabilises. This neutralises double and triple encoding in one pass without infinite-loop risk.
Anomaly Scoring: Ban by Accumulated Threat Score
Not every attack arrives in one request. Slow scanners probe a few paths, wait, probe more. A per-rule ban misses them. Anomaly scoring tracks each IP's cumulative threat score across all rule matches.
Every rule has a severity value:
- Critical (100) — Log4Shell, Shellshock, Spring4Shell, honeypot — instant ban on a single hit
- High (50–60) — SQL injection, XSS, command injection, SSRF, NoSQL — two hits ban the IP
- Medium (35–40) — scanner agents, header injection, open redirect — needs a few hits
- Low (10–20) — common probes, PHP file scans — requires sustained activity before ban
When an IP's accumulated score hits 50, it is banned at the network level via ipset without waiting for a critical-severity trigger. This catches sophisticated distributed probes that deliberately stay below single-rule thresholds.
IP Reputation Tracking
The new ip-reputation.ts library maintains a reputation record for every IP that triggers any rule. Records are stored in the waf_ip_reputation database table and cached in Redis with a 7-day TTL (waf:rep:<ip>).
Each reputation entry records the threat category, severity, last seen timestamp, and total request count. The admin security dashboard exposes an IP Intelligence modal showing this timeline for any blocked IP, and a "Top Threats" view of the most persistent sources.
Honeypot Traps
The honeypot rule type defines paths that no legitimate user would ever visit — /.env, /wp-admin, /phpmyadmin, /console, and similar. Any IP that sends a request to one of these paths is immediately placed on a 365-day ban. Honeypots generate zero false positives by design: real users don't probe these endpoints.
11 External Threat Feeds
The automated cron job (/api/cron/waf-scan, running every 2 minutes) now ingests 11 external threat intelligence feeds, including:
- Spamhaus EDROP / DROP
- IPsum Level 5 (known bad IPs)
- Tor Exit Nodes
- Firehol Level 1
IP ranges from these feeds are loaded into the scanners ipset and blocked at the kernel level before a single packet reaches the application.
Admin Dashboard Improvements
The WAF tab in the admin security panel was redesigned alongside the engine:
- Rules are displayed in collapsible sections by category with colour-coded severity badges
- A 24-hour attack timeline chart shows threat volume over time
- Four stat cards surface blocked IPs, total rule matches, active threat feeds, and recent bans
- The IP Intelligence modal shows the full history for any IP: rules triggered, timestamps, threat score progression
The WAF cron also fixed a feed seeding bug where existing feeds would prevent new ones from being added. The seeder now uses upsert-by-name so incremental additions work correctly.
What's Next
We're planning geographic blocking at the ingress level (beyond the existing UFW GeoIP rules) and a CAPTCHA challenge flow for medium-severity IPs before they are fully banned. The goal is to catch sophisticated probes even earlier, while giving legitimate users in high-risk geographies a path through.



