WAF Internals

APPLICATION SECURITY · DEEP DIVE
WAF Internals
How a Web Application Firewall Actually Decides
Not “what a WAF is” — how one parses a request, scores it, and decides to let it through or kill it, and exactly where that reasoning breaks down. Written for engineers who have to configure, tune, and defend the decision to run one.
01 · ARCHITECTURE
Where inspection actually happens
A stateful network firewall filters on IP address, port, and TCP/UDP session state — Layer 3/4. It has no opinion about what's inside a POST body, because at that layer there is no POST body yet, just packets. A WAF sits at Layer 7: it terminates or observes the HTTP(S) session, reconstructs the full request — method, path, query string, headers, cookies, multipart body — and evaluates that before the origin server ever sees it.
This is why a WAF has to sit at (or before) the point where TLS is terminated. An appliance or proxy that only sees encrypted bytes cannot inspect content; it can only do connection-level filtering, which a network firewall already does. Every WAF deployment decision starts from this constraint.
Fig. 1 — A network firewall filters connections; it has no visibility into request content. A WAF must sit at the point of TLS termination to read the payload at all.

02 · DETECTION THEORY
Positive vs. negative security models
Every WAF rule engine is built on one of two philosophies, and most production deployments end up blending both.
Negative security model (blocklisting) enumerates known-bad patterns — SQL keywords in unexpected places, script tags, directory traversal sequences — and blocks a request that matches one. This is how signature-based engines like the OWASP Core Rule Set work by default. It's low-friction to deploy against existing traffic because it only reacts to things that look like known attacks, but by construction it cannot catch a pattern nobody has written a signature for yet.
Positive security model (allowlisting) does the opposite: it defines the exact shape a legitimate request is allowed to take — parameter names, types, lengths, an API schema — and rejects anything that deviates, known attack or not. It catches zero-days for free, but it requires you to actually model your application's legitimate traffic, and it breaks the moment a developer ships a new field without updating the policy.
Fig. 2 — The negative model reacts to what it already knows; the positive model rejects everything it hasn't explicitly allowed. Coverage vs. maintenance burden is the actual trade-off.
Practical read: pure allowlisting is rare outside of narrow, well-specified APIs. Most production WAFs run a tuned negative model (CRS or a managed rule set) as the default, with positive-model rules hand-written for the handful of endpoints — auth, payment, admin — where the blast radius justifies the maintenance cost.
03 · MECHANISM
The request inspection pipeline
This is the part most write-ups skip: a WAF doesn't run one big regex against the raw bytes. A production engine — ModSecurity with the OWASP CRS is the reference implementation most others mirror — runs a request through distinct phases, and the decision at the end is a score, not a single yes/no rule hit.
Normalization. URL-decode (recursively, to catch double-encoding), case-fold, resolve Unicode homoglyphs and overlong UTF-8 sequences, collapse path segments (..%2f, %2e%2e/). Skipping this step is the single most common way real deployments get bypassed — see §5.
Parsing. Split the normalized request into typed collections: ARGS_GET, ARGS_POST, REQUEST_HEADERS, REQUEST_COOKIES, FILES (multipart parts get parsed individually, including nested JSON bodies if a content-type transform is configured).
Rule matching. Each collection is run against rule groups organized by attack class — SQLi, XSS, RCE/command injection, LFI/RFI, protocol anomalies, scanner/bot signatures. A single request can match rules across several groups simultaneously.
Anomaly scoring. Instead of blocking on the first match, each triggered rule adds points weighted by severity (CRS uses roughly: notice = 2, warning = 3, error = 4, critical = 5). Scores accumulate across the whole request.
Decision. At the end of the phase chain, the accumulated score is compared against a configured threshold (a common CRS starting point is 5 for inbound). Below threshold → forward to origin. At or above → block, typically a 403, with the matched rule IDs logged.
Fig. 3 — The ModSecurity/CRS pipeline: matches don't block individually — they accumulate into a request-wide anomaly score, which is what actually crosses the threshold.
This scoring design is why “paranoia level” tuning matters so much in CRS. Higher paranoia levels enable more aggressive, higher-recall rules that also raise false-positive rates — moving the threshold and paranoia level together is the actual tuning surface, not toggling individual rule IDs by hand (though you'll still do that for the rules that genuinely don't fit your application).

04 · DEPLOYMENT
Deployment topologies
The pipeline above is the same regardless of where it physically runs. Where you put it changes what it can see and what it can do when it decides to block.

Fig. 4 — The dashed line matters: an out-of-band WAF sees a mirrored copy of the request after the real one already reached origin. It can page you; it can't have blocked the request that just got through.
05 · WHERE IT BREAKS
Normalization and evasion
Almost every real-world WAF bypass is a normalization gap, not a missing signature. If the rule engine matches on the raw string but an attacker can express the same payload in a form the engine doesn't decode first, the rule simply never fires.
Common techniques, all of which exist specifically to survive step 1 of the pipeline in §3:
Double/mixed encoding — %2527 (encoded twice) or mixing %2f with literal / so a single decode pass leaves a still-encoded fragment.
Inline comments in SQL — /*!SEL*/ECT or UNI/**/ON SEL/**/ECT, which many database engines still parse as valid but which breaks a keyword-matching regex looking for the literal token.
Case and Unicode tricks — mixed-case keywords, full-width Unicode homoglyphs, or overlong UTF-8 sequences that decode to an ASCII character the naive matcher never canonicalized.
HTTP Parameter Pollution (HPP) — sending the same parameter twice (id=1&id=1%20OR%201=1); the WAF and the application framework can disagree about which occurrence “wins,” so the WAF inspects the benign one while the app processes the malicious one.
Multipart/charset tricks in file uploads — a payload with a spoofed Content-Type, an unusual boundary format, or a charset the parser doesn't fully support, so the part is forwarded unparsed rather than inspected.
Fig. 5 — Same payload, two outcomes. The difference is entirely in whether comments and whitespace get normalized away before the pattern match runs — this is the step attackers target, not the pattern itself.
Consequence for tuning: a WAF that scores well against an off-the-shelf scanner but has never been tested against hand-crafted, obfuscated payloads is largely untested. Run your own encoding/obfuscation variants through it before trusting the block rate.

06 · OPERATIONAL USE
Virtual patching
When a CVE drops for a library or framework you run, the real fix — upgrading the dependency, rebuilding, testing, deploying — routinely takes days to weeks, especially for anything customer-facing that needs a regression pass. A WAF rule targeting the specific exploit pattern can usually ship in hours. That gap is what “virtual patching” covers: a temporary, narrowly-scoped block at the edge that buys time without touching application code.
It is explicitly a stopgap, not a fix. The underlying vulnerable code is still vulnerable — the WAF rule only blocks the specific request shapes it was written to catch, and a sufficiently different exploitation technique for the same bug can slip past it.
Fig. 6 — The WAF rule's job is to shrink the exposure window between disclosure and the real fix — not to replace the real fix.
07 · SCOPE AND LIMITS
Defense in depth: what a WAF is not
Everything above describes a control that inspects requests. It has no model of your application's business logic, so it cannot catch a legitimate-looking request that abuses a legitimate feature — a privilege-escalation flaw exploited by submitting a normal-looking extra field, a broken object-level authorization check, a race condition in a checkout flow. Those requests are, structurally, indistinguishable from valid traffic; a WAF has nothing to pattern-match against.
It also can't fix configuration or code that hasn't been fixed: a session cookie missing HttpOnly/Secure, a document root that exposes more than the app's public directory, a file-upload path that executes what it stores, or a missing Content-Security-Policy. Those are separate controls, and the WAF sitting in front of them doesn't make them correct.
Fig. 7 — The WAF is one ring in the stack, positioned early enough to stop a lot of noise — it is not a substitute for any ring behind it.
Rule of thumb: if the fix belongs in your code or your server config, put it there. Route a WAF rule around it only as a stopgap (see §6), never as the permanent home for a fix that belongs downstream.
08 · OPERATIONS
Rollout maturity and the feedback loop
The single most common production mistake is switching a WAF straight to blocking mode on day one. Default rule sets are tuned against generic traffic, not yours, and will false-positive against legitimate requests your application actually sends — a long JSON body that trips a size rule, a rich-text field that looks like an XSS payload, an internal API client with an unusual header. Roll out in stages instead.
Detection only. Rules evaluate and log, nothing is blocked. Baseline what your real traffic looks like against the rule set.
Tuning. Work through the false positives: exclude specific rule IDs for specific paths/parameters (never disable a rule globally when a scoped exception will do), adjust the anomaly threshold, add positive-model rules for high-value endpoints.
Selective enforcement. Turn on blocking for the highest-confidence rule groups first (obvious SQLi/RCE signatures), while lower-confidence categories stay in log-only.
Full enforcement. Blocking is live across rule groups, thresholds are stable, and false-positive rate is at an accepted baseline.
Continuous tuning. New endpoints, new attack signatures, and drift in traffic patterns all require the loop to keep running — this stage never ends.
Fig. 8 — The loop back into tuning isn't a failure state — it's the operating model. A WAF that reaches “full enforcement” and is never revisited will drift out of sync with both your application and the current threat landscape.
The logs from this loop are worth routing to whatever you already use for security monitoring (SIEM, or even a structured log pipeline) rather than only the WAF's own console — correlating WAF block/alert events with application logs and auth events is usually how you catch a coordinated attack, not any single blocked request in isolation.
Closing note
A WAF earns its place by sitting early in the request path and doing one job well: reading HTTP content that a network firewall structurally cannot see, and scoring it against known-bad and known-good shapes. It does not replace secure session handling, validated file uploads, parameterized queries, or correct authorization checks — those still have to be right at the source. Treat it as a fast, tunable filter that buys time and cuts noise, wire its logs into whatever you use for detection, and revisit the tuning on a real cadence rather than treating “enforcement mode” as a finished state.
Diagrams are schematic — drawn to show the mechanism at each stage, not to represent any specific vendor's exact internal implementation.
Rate this article
Be the first to rate this article
Comments & Discussion0
Related articles
cPanel Explained
Technical TutorialscPanel Explained
A Technical Deep Dive into the Hosting Control Panel That Runs Millions of Websites
aiman al-murish

