CSP does not use unsafe-inline for scripts
Why it matters
'unsafe-inline' in script-src means any <script> tag injected by an attacker — through XSS, a compromised third-party dependency, or a DOM mutation — runs with full page context. It effectively negates CSP's primary purpose: a policy exists but an XSS payload executes anyway. OWASP A03 (Injection) flags this as a common misconfiguration where developers add a CSP for compliance but inadvertently include unsafe-inline to silence warnings, creating a security theater scenario. CWE-79 (XSS) and CWE-1336 (improper neutralization) both apply. The correct fix — cryptographically random nonces per request — is supported natively by all major frameworks.
Severity rationale
High because `unsafe-inline` in `script-src` defeats XSS protection even when a CSP is present, reducing it to security theater.
Remediation
Replace 'unsafe-inline' with per-request cryptographic nonces in middleware.ts. The nonce must be random on every request — static nonces provide zero protection.
// middleware.ts
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
export function middleware(request: NextRequest) {
const nonce = Buffer.from(crypto.randomUUID()).toString('base64')
const csp = `default-src 'self'; script-src 'self' 'nonce-${nonce}'`
const response = NextResponse.next()
response.headers.set('Content-Security-Policy', csp)
response.headers.set('x-nonce', nonce)
return response
}
Pass the nonce to <Script nonce={nonce}> components via the request headers. Move any remaining inline scripts to external files or use hash-based allowlisting if the inline content is truly static.
Detection
-
ID:
csp-no-unsafe-inline -
Severity:
high -
What to look for: If a CSP is present, enumerate all directives in the policy and examine the
script-srcdirective for'unsafe-inline'. Note: Next.js uses nonce-based inline scripts by default, which is acceptable withoutunsafe-inline. -
Pass criteria: CSP
script-srccontains 0 occurrences of'unsafe-inline', OR uses nonce-based ('nonce-*') or hash-based ('sha256-*') inline script allowlisting that overrides the unsafe-inline directive. -
Fail criteria: CSP includes at least 1
'unsafe-inline'inscript-srcwithout nonce or hash supplementation. -
Skip (N/A) when: Skip when
csp-presentresult isfailorskip— there is no CSP to evaluate for unsafe-inline directives. -
Detail on fail:
"CSP script-src includes 'unsafe-inline' which defeats much of CSP's XSS protection"or"CSP uses 'unsafe-inline' and 'unsafe-eval' in script-src" -
Remediation:
unsafe-inlineinscript-srcallows any inline script to run, which largely defeats CSP's purpose. Instead, use nonces or hashes. In Next.js, generate a cryptographically random nonce per-request in middleware:// middleware.ts — generate a nonce per request import { NextResponse } from 'next/server' import type { NextRequest } from 'next/server' export function middleware(request: NextRequest) { const nonce = Buffer.from(crypto.randomUUID()).toString('base64') const csp = `default-src 'self'; script-src 'self' 'nonce-${nonce}'` const response = NextResponse.next() response.headers.set('Content-Security-Policy', csp) response.headers.set('x-nonce', nonce) // pass to server components return response }The nonce value must be cryptographically random and unique per request — never use a static nonce value, as that provides no security benefit. See the Next.js CSP documentation for the complete
generateCspNonce()pattern and how to pass the nonce to<Script>components.If you have inline scripts, either move them to external files or use the nonce approach your framework provides.
External references
- cwe · CWE-79 — Improper Neutralization of Input During Web Page Generation (XSS)
- cwe · CWE-1336 — Improper Neutralization of Special Elements Used in a Template Engine
- owasp:2021 · A03 — Injection
- owasp:2021 · A05 — Security Misconfiguration
Taxons
History
- 2026-04-17·v1.0.0·Initial import from security-headers·automated