← Glossary

Protocols & Acronyms

CSP (Content Security Policy)

Also: Content Security Policy · Content-Security-Policy header · script-src · CSP nonce · strict-dynamic · frame-ancestors

In one sentence

CSP is an HTTP response header that tells the browser which sources a page may load scripts, styles, images and frames from — so an injected script tag, even one that reaches the HTML, does not run.

Why it matters

Cross-site scripting has been on every top-ten list for twenty years because input validation always misses one field. CSP is the defence that works after the miss: the attacker's <script> lands in the page, and the browser refuses to execute it because the policy did not authorise it. The same header stops your page being framed for clickjacking (frame-ancestors), blocks data exfiltration to unknown hosts (connect-src), and — the reason PCI DSS v4 requirements 6.4.3 and 11.6.1 exist — keeps a payment page from loading a skimming script from a compromised third-party CDN.

It is also one of the few controls with a built-in feedback loop: report-to sends the browser's violation reports to you, so a deployment in report-only mode tells you what would break before anything does.

How a policy reads

Content-Security-Policy: default-src 'self';
    script-src 'nonce-r4nd0m…' 'strict-dynamic';
    style-src 'self' 'unsafe-inline';
    img-src 'self' data:;
    connect-src 'self' https://api.example.com;
    frame-ancestors 'none';
    base-uri 'self';
    form-action 'self';
    report-to csp-endpoint
  • Directives name a resource type: script-src, style-src, img-src, connect-src (XHR/fetch/WebSocket), frame-src, font-src, object-src. default-src is the fallback for any not listed.
  • Sources: 'self' (same origin), a host or scheme, 'none', and the keywords everyone misreads — 'unsafe-inline' (allow inline scripts/styles — defeats the point for scripts) and 'unsafe-eval'.
  • Nonces and hashes. A per-response random 'nonce-…' on the header and on each legitimate <script nonce="…"> means only scripts the server put there run. 'sha256-…' does the same for a fixed inline script. With 'strict-dynamic', a nonced script may load further scripts, and host allow-lists are ignored — the modern "strict CSP" recipe.
  • frame-ancestors replaces X-Frame-Options; base-uri and form-action close two injection side doors; upgrade-insecure-requests fixes mixed content.
  • Content-Security-Policy-Report-Only is the same header in dry-run mode: nothing blocked, everything reported.

This site runs a nonce-based policy on every page; the nonce rotates per request, which is why its pages are never cached as HTML.

Try it yourself hands-on

Your marketing site has one third-party analytics script, fonts from Google, and no CSP. A pentest report marks XSS as high because there is nothing to contain it.

  1. Paste the site's current response headers into the HTTP Security Headers Grader. Expect a low grade with "Content-Security-Policy: missing" at the top of the findings, plus whatever else is absent (HSTS, X-Content-Type-Options).
  2. Open the CSP Builder and start from the strict preset. Set script-src to a nonce with 'strict-dynamic', add https://fonts.googleapis.com to style-src and https://fonts.gstatic.com to font-src, connect-src 'self' https://analytics.example, frame-ancestors 'none'. The builder assembles the header and flags 'unsafe-inline' in script-src as the weakening it is.
  3. Append a report-to (or legacy report-uri) directive pointing at a collector, and deploy the result as Content-Security-Policy-Report-Only first. After a week, the reports show what you forgot — typically an inline onclick handler and an image from a CDN. Fix the handler (move it to a nonced script), add the CDN to img-src.
  4. Switch the header to enforcing. Re-grade with the Headers Grader: CSP now present, grade up. For the analytics script itself, generate a Subresource Integrity hash with the SRI Generator so a changed file on the vendor's CDN is refused even though its host is allowed.

Common misreadings

  • A host allow-list is weaker than it looks. Allowing https://cdn.example allows every file on it, including the JSONP endpoint an attacker can abuse. Nonces plus 'strict-dynamic' are why "strict CSP" exists.
  • 'unsafe-inline' in script-src switches CSP off for XSS purposes. It is acceptable in style-src as a transition, not in script-src.
  • Report-only forever is not a control. Sites launch in report-only "for a week" and stay there for years. Put the enforcement date in the ticket.