Skip to content

WAF filters and XSS: what breaks in production

WAFs can block noisy payloads, but real XSS testing needs to account for normalization, false positives, parser gaps, and operational pressure.

Published on 2 min read

WAFs are good at blocking things that look like public payload lists.

That is not an insult. Blocking commodity probes is useful. The problem starts when teams confuse WAF behavior with application security. A WAF can reject <script>alert(1)</script> all day and the app can still be one unsafe innerHTML assignment away from trouble.

Production makes this messier.

Normalization is where bugs hide

Every layer has opinions about input. The CDN decodes URL components. The load balancer normalizes duplicate slashes. The application framework parses query strings. The WAF inspects one representation. The browser eventually parses another.

This gap is where bypasses live.

client sends:  %253Csvg%2520onload%253Dalert(1)%253E
waf sees:      %3Csvg%20onload%3Dalert(1)%3E
app renders:   <svg onload=alert(1)>

That example is simple. Real stacks add JSON parsing, reverse proxies, legacy encodings, template engines, and frontend hydration.

False positives shape the rules

Security engineers rarely get to write perfect WAF rules. They inherit payment flows, search boxes, rich text editors, support tools, and angry product owners. A strict rule that blocks < everywhere may break legitimate HTML previews. A rule that blocks onerror may catch documentation, code samples, or customer content.

So teams add exceptions.

Then exceptions become policy.

That is why testing should include blocked tokens and filter profiles. If a target blocks spaces, angle brackets, quotes, or specific keywords, the payload builder should make that visible. Not as a magic bypass machine, but as a way to reason about constraints.

Bypass payloads need context

Case changes, tab insertion, string concatenation, entity encoding, and broken tag construction can all matter. They can also be nonsense if the injection context does not support them.

This is the difference between testing and theater:

<svg/onload=alert(1)>

Useful in some parser paths. Meaningless in others. If the input is inserted through textContent, it is just text. If a sanitizer canonicalizes tags before filtering, the outcome changes again.

What to report

A good WAF-related XSS report separates three things: the vulnerable application behavior, the WAF behavior, and the browser execution path. Do not just paste the final payload.

Include the blocked baseline if it helps:

baseline blocked: <img src=x onerror=alert(1)>
working variant:  <svg/onload=alert(1)>
context:          unquoted HTML attribute after server-side template render

That gives the application team something to fix and the security team something to tune. Both are needed.

Related articles

Responsible XSS testing depends on scope, evidence discipline, data minimization, and knowing when a proof of concept has gone far enough.
A practical look at why XSS tooling should avoid server-side processing when testers are handling internal URLs, private snippets, and sensitive payload notes.
Payload choice is less important than injection context. HTML body, attributes, URLs, scripts, and DOM sinks all fail differently.