A critical CVE lands. Someone asks in the team chat whether we are exposed, and the first reply is “we have a WAF, we’re covered.”
I understand the reflex. A WAF is one of the fastest ways to react, and I would not want to run a public application without one. But the sentence mixes up two different things. A WAF changes the odds that an attacker reaches a bug. A patch removes the bug. When the first gets treated as the second, a temporary bridge quietly becomes permanent infrastructure.
What a WAF is good at
A WAF inspects HTTP requests before they reach your application and decides what to do with them: block, challenge, rate-limit or log. That gives you a lot. It absorbs scanner and bot noise, it gives you a central place for logs and visibility, and it lets you ship an emergency rule to the edge in minutes instead of rolling a release across a fleet.
That speed is the real value. OWASP’s Virtual Patching Cheat Sheet describes the goal as minimizing time-to-fix by blocking the known exploitation path while a permanent fix is prepared. If you are exposed on a Tuesday afternoon and the vendor patch needs testing, a virtual patch is the right first move.
What a virtual patch does not do
The same OWASP page is blunt about the limit. A virtual patch blocks exploitation without changing the vulnerable code, so it “does not remove the underlying vulnerability.” It also says fixing the source code is the number one remediation strategy, and that the two efforts are not mutually exclusive: they run in parallel, usually owned by different teams. One passing payload test, it adds, does not establish complete protection.
Vendors that sell this say the same. F5 writes that virtual patching isn’t a replacement for real patching, but an essential layer that buys you and your development teams time, and that a virtual patch may be bypassed if its rules are incomplete. Its virtual patching overview is just as direct: it is not a replacement for code remediation, and the two work together. Cloudflare’s own WAF explainer notes that a WAF is not designed to defend against all types of attacks and usually sits within a suite of tools. It also points out that a blocklist-style WAF only protects against known attacks.
That last point matters most. A virtual patch is a signature of a known exploit shape. It is not a fix for the vulnerability class. A narrow rule misses variants, and a broad rule blocks real users.
How WAFs get bypassed
Attackers only need one variant that gets through. Two recent pieces of research show how that happens, and in both the root cause is the same: the WAF and the application parse the same request differently.
Request structure. The WAFFLED paper (Akhavani et al., 2025; also published at ACSAC 2025) reports 1,207 bypasses across five well-known WAFs: AWS, Azure, Cloud Armor, Cloudflare and ModSecurity. The attack payload stays unchanged. Only the structure of the request is mutated, through things like multipart boundaries, XML namespaces and JSON, so the WAF misreads the body while the framework parses it without trouble. The authors also found that more than 90% of the sites they sampled accept both urlencoded and multipart bodies interchangeably, which makes the trick widely applicable. Their conclusion is that WAF users may falsely believe they are protected.
Legacy parsing. PortSwigger’s Zakhar Fedotkin showed a bypass using the phantom $Version cookie (published December 2024, updated June 2025). Legacy RFC 2109 cookie parsing lets quoted or encoded payloads slip past signatures. In the AWS WAF example, a rule blocks eval('test') but lets the octal-escaped form through. Frameworks such as Flask, Django, PHP, Ruby and Spring also handle repeated Cookie headers differently, which gives attackers more room to hide a payload. The fix PortSwigger recommends is in the application: validate input and disable legacy cookie parsing. A stronger WAF rule is not the recommendation.
Neither finding says WAFs are useless. They say a WAF sees a copy of reality, and the application sees the real thing. Any gap between the two is where the exploit lives.
A WAF can be part of the failure
The 2019 Capital One breach is the usual cautionary tale, and it is worth reading carefully. Brian Krebs reported that a misconfigured open-source ModSecurity WAF let the attacker trick the firewall into relaying requests to the AWS metadata service, and that the WAF’s role had enough permissions to list and read every storage bucket.
Details of that story are contested. AWS attributed the incident to a firewall misconfiguration and an overly broad role, and analysts disagree about the exact attack path, so I would not use it as proof that “the WAF failed to patch a bug.” The better lesson comes from the peer-reviewed systematic analysis in ACM. It identifies five distinct control failures: the misconfigured WAF, the metadata service, an over-provisioned IAM role, ineffective encryption and ineffective monitoring. In the authors’ words, the “misconfigured firewall” was just the tip of the iceberg and the breach was a systems failure. Any one of those controls, properly enforced, would have stopped the attack or limited the damage.
A WAF is one layer. It also needs care of its own: OWASP notes that tailoring a WAF to an application can be significant work and has to be maintained as the application changes.
What engineering teams still owe
The WAF protects the application. The team protects the system the WAF sits in front of. In practice that means:
- Fix the root cause. Input validation, parameterized queries, output encoding and least privilege are not made redundant by an edge rule. They are what make the edge rule unnecessary.
- Keep runtimes and dependencies current. Know what you run, and agree on a time limit for critical CVEs. End-of-life versions are the worst case: no upstream fix exists, so the “temporary” rule becomes permanent.
- Test the fix, not only the rule. Add a regression test for each fixed vulnerability, and treat a blocked proof-of-concept as one data point, not as a verdict.
- Think about the trust boundary. Ask what happens if the edge is bypassed, and what the application’s own credentials can reach if it is compromised. The Capital One analysis is that question answered the hard way.
Using a WAF well
- Patch first, WAF second. Treat a virtual patch as a bridge with an owner and an expiry date, tracked next to the real fix.
- Move from alert to deny. Measure false positives, then enforce. Rules that stay in detect-only mode protect nothing.
- Lock down the origin. If an attacker can reach the origin directly, the WAF is optional for them. Allow only your edge to connect.
- Prefer vulnerability-specific rules with generic ones around them. Never rely on a single indicator, such as a User-Agent string, that an attacker can change in seconds.
- Re-test after changes. Test with the real exploit shape in staging, and again after rule updates, including the structural variants described above.
- Watch behind the edge. Send WAF events to your monitoring and look for post-exploitation signals on the host, not only at the perimeter.
- Review exceptions on a schedule. Rules loosened after a false positive tend to stay loose.
Closing
Use the WAF. When a serious vulnerability drops, a rule at the edge in the first hour is good engineering, and a team that cannot do that is slower than it needs to be.
Then spend the time it bought. This week, list the findings your WAF is currently “covering,” and put an owner and a date on each real fix. Check that your origin is not reachable around the edge, and that your rules are in deny. The WAF buys time, and the patch is what ends the problem.
References
- OWASP, Virtual Patching Cheat Sheet and Web Application Firewall.
- F5, Virtual patching in practice and Virtual patching.
- Cloudflare, What is a WAF?
- Akhavani et al., WAFFLED: Exploiting Parsing Discrepancies to Bypass Web Application Firewalls.
- Zakhar Fedotkin, PortSwigger Research, Bypassing WAFs with the phantom $Version cookie.
- Brian Krebs, What We Can Learn from the Capital One Hack.
- ACM, A Systematic Analysis of the Capital One Data Breach: Critical Lessons Learned.