Security Hardening: Nonce-Based CSP Replaces unsafe-inline and unsafe-eval
Security Hardening: Nonce-Based CSP Replaces unsafe-inline and unsafe-eval
Release: v0.1.4
Compliance framework: ISO/IEC 27001 — Control ISO-01
Affected file: next.config.ts, middleware.ts
Background
Content Security Policy (CSP) is a browser-enforced mechanism that controls which scripts, styles, and other resources a page is permitted to load and execute. A well-configured script-src directive is one of the most effective mitigations against Cross-Site Scripting (XSS) attacks.
Prior to v0.1.4, the application's CSP included two directives that significantly undermined this protection:
| Directive | Risk |
|---|---|
'unsafe-eval' | Permits eval(), new Function(), and similar runtime code evaluation. Any code path — including attacker-injected content — can execute arbitrary JavaScript. |
'unsafe-inline' | Permits inline <script> tags and javascript: URLs. This is the primary vector for reflected and stored XSS payloads. |
With both directives present, the CSP provided no meaningful protection against script injection: a successful XSS attack would have executed without any browser-level block.
What Changed
'unsafe-eval' Removed
'unsafe-eval' is only required when application code calls eval(), new Function(), setTimeout(string), or similar constructs. No such patterns exist in this codebase, so the directive has been unconditionally removed.
'unsafe-inline' Replaced with Per-Request Nonces
Rather than permitting all inline scripts, the application now generates a cryptographically unique nonce on every HTTP request. Only <script> elements that carry the matching nonce attribute are permitted to execute.
How it works:
middleware.tsgenerates a random nonce for each incoming request.- The nonce is written into the
Content-Security-Policyresponse header:Content-Security-Policy: script-src 'self' 'nonce-<random-value>' - The nonce is passed to Next.js via the
next/headersAPI, using Next.js 15's built-in nonce support. - Next.js automatically stamps the nonce onto all framework-managed
<script>tags in the rendered HTML. - Any injected script that does not carry the correct nonce — including attacker-controlled content — is blocked by the browser before execution.
Because the nonce changes on every request, an attacker cannot pre-compute or reuse a valid nonce even if they can observe a previous response.
Compliance Alignment
This change directly addresses ISO/IEC 27001 Control ISO-01 requirements for application-layer security controls. A CSP containing 'unsafe-inline' or 'unsafe-eval' is considered a misconfiguration under this control framework, as it negates the stated security benefit of the header.
Impact on Developers
- No
eval()usage: If future code requires dynamic evaluation, raise a security review before re-introducing'unsafe-eval'. - Inline scripts: Do not add raw
<script>blocks to page markup. Use Next.js<Script>components or module imports — these will receive the nonce automatically. - Third-party scripts: Any third-party
<script>tag added to the application must be loaded via the Next.js<Script>component so the nonce is applied. Inline event handlers (onclick="...") are not permitted under the new policy. - Testing: Browser developer tools will display CSP violations in the console. A blocked script that should be permitted indicates a missing nonce — fix the injection mechanism rather than relaxing the policy.