Content Security Policy: writing one that actually protects

A Content-Security-Policy tells the browser which scripts it may run. Done well, an injected <script> simply does not execute. Done loosely, the header is there and protects nothing.

Example

Weak
Content-Security-Policy: default-src * 'unsafe-inline' 'unsafe-eval'
Strict
Content-Security-Policy: default-src 'self'; script-src 'self' 'nonce-{random}' 'strict-dynamic'; object-src 'none'; base-uri 'self'; frame-ancestors 'self'; upgrade-insecure-requests

What weakens a CSP

'unsafe-inline' in script-src allows exactly the inline scripts and event handlers an injection produces. 'unsafe-eval' lets strings become code. Sources such as *, https: or data: allow script from any host. A policy with no script-src or default-src does not restrict scripts at all, and a report-only policy blocks nothing. Missing object-src 'none' and base-uri leave two older bypasses open.

How to build one

Start strict in report-only mode, fix what it reports, then enforce.

  • Use a per-response nonce on your scripts, plus 'strict-dynamic' for the scripts they load
  • Add object-src 'none'; base-uri 'self'; frame-ancestors 'self'
  • Move inline event handlers into script files
  • Send it as a header — frame-ancestors does not work in a meta tag

How RepoVerse finds it

  • ModerateNo Content-Security-Policy

    If any page has an injection bug, the injected script runs with nothing to stop it — CSP is the layer that limits what one XSS can do.

    website · CWE-693

  • LowContent-Security-Policy is only reported, not enforced

    A report-only policy logs violations but blocks nothing, so it gives no protection yet.

    website · CWE-693

  • ModerateCSP allows inline scripts

    `'unsafe-inline'` in `script-src` lets injected `<script>` tags and event handlers run, which is the main thing a CSP is there to stop.

    website · CWE-79

  • LowCSP allows eval

    `'unsafe-eval'` lets strings become code through `eval`, `new Function` and string timers, which turns some injection bugs into script execution.

    website · CWE-95

  • ModerateCSP lets scripts load from anywhere

    A `script-src` of `*`, `https:`, `http:` or `data:` allows script from any host, so an attacker who can inject a tag can load their own file and the policy will allow it.

    website · CWE-693

  • LowCSP leaves plugins or the base URL open

    Without `object-src 'none'` old plugin content can still execute; without `base-uri` an injected `<base>` tag can redirect every relative script URL on the page.

    website · CWE-693

Questions

Does a nonce make 'unsafe-inline' safe?
When a nonce or hash is present, CSP Level 2 browsers ignore 'unsafe-inline', so keeping it only helps very old browsers. RepoVerse does not report 'unsafe-inline' when a nonce or hash is in the policy.
Can I use CSP with Next.js or React?
Yes. Next.js supports nonces through middleware, and React renders without inline scripts by default. Third-party tags are usually what make a strict policy hard.

Related

Updated 2026-10-11