CSP Academy

← The ten steps · Step 1 of 10 · 3 minutes

What CSP actually does

A Content Security Policy is a response header in which you tell the browser where this page is allowed to load things from. The browser enforces it. Nothing else has to change.

The smallest useful example

Content-Security-Policy: default-src 'self'

That says: for this page, load scripts, styles, images, fonts and everything else only from the origin the page came from. A script tag pointing anywhere else is refused before a request is made — no network call, no execution, an entry in the console.

What it is for

CSP exists because of one hard problem: when an attacker gets content onto your page, the browser cannot tell their script from yours. Both arrive in the same HTML, from the same origin, with the same authority. CSP gives you a way to declare, out of band in a header the attacker cannot write, which scripts are really yours.

That is why the useful parts of a policy are all about script. Everything else — images, fonts, styles — is worth locking down, but the reason CSP exists is script execution.

The one thing it is not

CSP is not a replacement for output encoding, input validation or a decent framework. It is the layer that catches the bug you missed. A site with no XSS bugs does not need CSP; nobody knows whether they are that site, which is the point.

You will see people argue that because CSP can be bypassed it is not worth deploying. Almost every published bypass is a bypass of a specific weak policy — a CDN that hosts a JSONP endpoint, an allowlist with an upload host in it, 'unsafe-inline' left in for a marketing tag. Those are worth knowing, and this course covers them, because knowing them is how you write a policy that does not have them.

Where the policy goes

Send it as an HTTP response header. You can also deliver a policy in a <meta http-equiv> tag, but it is only enforced from the byte the parser reads it — anything above it in the document has already run unprotected — and it silently ignores frame-ancestors, report-uri and sandbox. Use the header.

See it happen: Script injection

The clearest way to understand the last four paragraphs is to watch them. This page has a script injection on it. Load it once with no policy and the injected script runs; load the identical page with the policy on and the browser refuses it.

Identical page both times — the only difference is the Content-Security-Policy header. On the Report URI demo site.

You can do this now

Open your site, press F12, and look at the response headers on the main document. Is there a Content-Security-Policy at all? If there is, paste it into the analyser and see what it says. Either answer is a fine place to start from.