Content Security Policy (CSP)
In web security, Content Security Policy (CSP) is a response header that tells the browser which sources of script, style, frames and other resources a page is allowed to use. It is a containment control: it does not stop an injection happening, it limits what the injected content is permitted to do.
How it works
The server sends a policy as a list of directives, each naming the origins allowed for a resource type. The browser enforces it and refuses anything outside the list, optionally reporting violations to an endpoint you nominate. The directives that carry the weight are script-src, which decides what may execute, object-src, which should be empty, base-uri, which stops an injected base tag redirecting relative script URLs, and frame-ancestors, which controls who may frame the page and supersedes the older framing header for clickjacking.
A modern policy does not allowlist domains. It marks the scripts the page intends to run with a per-response nonce or a hash, so injected script has no nonce and does not run, and uses strict-dynamic so scripts loaded by trusted script inherit trust without the policy having to know every host.
What goes wrong
Most of the policies we meet are decorative. The single most common finding is 'unsafe-inline' in script-src, which permits exactly the injected inline script the policy exists to block, usually added to make an analytics tag work. Next is a nonce that is not per response, because a nonce an attacker can predict or reuse is not a nonce. Then allowlists so broad that they contain a host serving an outdated framework or a user-content platform, which gives the attacker a trusted place to host their payload.
The framing consideration is separate and often missed: a policy can be immaculate for script and still omit frame-ancestors. And a policy left in report-only mode after the rollout enforces nothing at all, which is a common state to find months after the project that introduced it ended. CSP is defence in depth for cross-site scripting, never a substitute for output encoding.
Where this shows up in an audit
The policy belongs in the hardening baseline, so we read it per response rather than from documentation, since it often varies by route, and we prove the consequence: an injection that the policy stops is one finding, an injection the policy waves through is another. The report lists the directives, the specific weakness, and a concrete payload that the current policy permits. Fixing the header without fixing the injection changes severity but not the finding. This is part of how we assess browser-side controls on a web application.