ethicalhacker.ro logoethicalhacker.ro
← Blog

Why CSP + Nonces Stop 90% of XSS in Modern Apps

2024-11XSSCSPWeb Security

Content Security Policy with per-request nonces is the single highest-leverage XSS defence available to web apps today. Here is how it works and why scanners keep missing it.


Why CSP + Nonces Stop 90% of XSS in Modern Apps

Cross-site scripting remains the most common high-severity finding in web application pentests. Developers patch individual injection points, but attackers find new ones. CSP with nonces shifts the economics: instead of patching every sink, you remove the payoff for the entire class of attack.

How Nonce-Based CSP Works

A nonce (number used once) is a random value generated fresh on every HTTP response. The server embeds it in the Content-Security-Policy header and in every
`

The browser only executes scripts whose nonce attribute matches the CSP header value. An attacker injecting cannot know next request's nonce — their payload is dead on arrival.

Why It Stops XSS Even When Sinks Exist

Classic XSS defences try to prevent untrusted data from reaching execution contexts. Nonce-based CSP accepts that sinks will occasionally exist and removes execution capability instead. A reflected XSS payload with no valid nonce is rendered as inert text by the browser.

Common Implementation Mistakes

1. Reusing nonces across requests. The nonce must be cryptographically random and unique per response. A static nonce is equivalent to 'unsafe-inline'.

2. Allowing 'unsafe-eval' alongside nonces. Eval-based code paths (some templating engines, legacy jQuery plugins) bypass the nonce entirely. Audit eval, Function(), setTimeout(string) calls.

3. Not covering style-src. CSS injection can exfiltrate data via @import or attribute selectors. Apply nonces to style tags too, or use hashes.

4. Forgetting object-src 'none' and base-uri 'self'. A tag injection redirects all relative URLs and defeats same-origin controls.

Deployment Checklist

The 10% That Remain

Nonces don't cover DOM-based XSS where JavaScript writes attacker-controlled data directly to innerHTML or eval()`. These require output encoding and trusted-types enforcement — a separate but complementary control.

More ArticlesRequest a Pentest
Why CSP + Nonces Stop 90% of XSS in Modern Apps | ethicalhacker.ro