How CSP Nonces Protect Inline JavaScript Execution
Content Security Policy (CSP) nonces protect inline JavaScript
execution by establishing a dynamic, cryptographic trust model between
the web server and the browser. Instead of broadly allowing all inline
scripts or maintaining complex hash lists, the server generates a
unique, unpredictable token—a “number used once” (nonce)—for every HTTP
response. Legitimate inline <script> tags must carry
this exact token to execute. Consequently, if an attacker attempts to
inject malicious inline script via Cross-Site Scripting (XSS), the
browser automatically blocks the unauthorized code because it lacks the
valid, per-request nonce.
The Threat of Inline JavaScript
Inline scripts are historically the primary attack vector for Cross-Site Scripting (XSS). Without specific safeguards, a web browser cannot distinguish between a legitimate inline script written by the application developer and an untrusted script injected by an attacker through user inputs, URL parameters, or compromised databases.
Earlier CSP implementations relied heavily on the
'unsafe-inline' directive, which effectively disables
inline script protection, leaving applications vulnerable.
How Nonce-Based CSP Works
Nonce-based execution follows a strict four-step lifecycle on every HTTP request:
Generation: When a user requests a web page, the web server generates a cryptographically secure, random string (e.g., a base64-encoded 128-bit value).
Policy Delivery: The server includes this token in the HTTP response header:
Content-Security-Policy: script-src 'nonce-EDNnf03nceIOfn39fn3e9h3sdfa'Markup Injection: The server injects the identical token into the
nonceattribute of all legitimate inline script elements within the rendered HTML:<script nonce="EDNnf03nceIOfn39fn3e9h3sdfa"> console.log("Trusted application code executed."); </script>Browser Verification: As the browser parses the HTML, it compares the
nonceattribute of each<script>tag against the value defined in theContent-Security-Policyheader. If the values match, the browser executes the script; if the attribute is missing or mismatched, execution is halted immediately.
Why Nonces Prevent Injected Attacks
- Unpredictability: Because the nonce is generated dynamically using a cryptographically secure pseudo-random number generator (CSPRNG), an attacker cannot guess the token in advance to include it in a stored or reflected payload.
- Single-Request Scope: A nonce is valid only for the specific HTTP response in which it was sent. Reusing previous tokens or attempting replay attacks fails because a new token is minted for subsequent requests.
- DOM Hiding: Modern browsers implement nonce-hiding
mechanisms. Once parsed, the
noncecontent attribute is removed or hidden from the DOM to prevent attackers from reading it via script or CSS-based data exfiltration techniques before injecting further code.
Implementation Essentials
To maintain effective protection: * Never reuse nonces across
multiple HTTP requests or cache pages containing static nonces. * Ensure
all random tokens originate from a CSPRNG rather than standard
pseudo-random functions like Math.random(). * Combine the
nonce directive with 'strict-dynamic' in modern CSP (Level
3) setups to automatically propagate trust to scripts dynamically loaded
by trusted inline scripts.