Security Concepts9 min read10 October 2026

What Is Content Security Policy (CSP)? Web Security Guide | Yrzo AI

Content Security Policy is a browser security mechanism that prevents XSS and data injection attacks. Learn how CSP works, how to write effective policies, and common pitfalls.

By Yrzo AI — UK cybersecurity specialists

What Is Content Security Policy?

Content Security Policy (CSP) is an HTTP response header that instructs browsers to enforce restrictions on what resources a page is allowed to load. By declaring a policy, you can prevent your web application from executing injected scripts, loading resources from untrusted origins, or submitting data to unexpected destinations — dramatically reducing the impact of cross-site scripting (XSS) and data injection attacks.

CSP is one of the most effective mitigations available against XSS, which consistently ranks in the OWASP Top 10. Despite this, the majority of web applications either have no CSP at all, or have a policy so permissive it provides no meaningful protection. Understanding CSP — how it works, how to write it correctly, and where implementations go wrong — is essential knowledge for anyone responsible for web application security.

How CSP Works

When a browser receives a response with a `Content-Security-Policy` header, it reads the policy and applies it to the page being loaded. The policy defines a set of directives, each controlling a different type of resource.

A simple but meaningful policy looks like this:

``` Content-Security-Policy: default-src 'self'; script-src 'self'; style-src 'self' https://fonts.googleapis.com; img-src 'self' data:; font-src 'self' https://fonts.gstatic.com; connect-src 'self'; frame-ancestors 'none'; base-uri 'self'; form-action 'self' ```

Breaking this down:

- **`default-src 'self'`** — Fallback for unspecified resource types. Only load from the same origin. - **`script-src 'self'`** — Only execute scripts from the same origin. Inline scripts are blocked. External scripts require the host to be listed. - **`style-src 'self' https://fonts.googleapis.com`** — Allow stylesheets from the same origin and Google Fonts. - **`img-src 'self' data:`** — Images from the same origin, plus data URIs (for inline images). - **`frame-ancestors 'none'`** — Prevents the page from being embedded in iframes (replaces X-Frame-Options). - **`form-action 'self'`** — Forms can only submit to the same origin. - **`base-uri 'self'`** — Prevents base tag injection, which could redirect relative URLs.

If a page protected by this policy contains injected JavaScript — from a stored XSS payload, a reflected parameter, or a compromised third-party script — the browser blocks it from executing, logs a violation, and the attack fails.

Directives Reference

The most important CSP directives for web application security:

**`script-src`** — Controls JavaScript execution. This is the most critical directive for XSS prevention.

**`style-src`** — Controls CSS loading. Inline styles should generally be blocked.

**`img-src`** — Controls image sources.

**`connect-src`** — Controls where XHR, Fetch, WebSocket, and EventSource connections can go. Restricting this prevents exfiltration of stolen data.

**`frame-ancestors`** — Controls which origins can embed the page in an iframe. Use `'none'` to prevent clickjacking.

**`form-action`** — Restricts where forms can submit. Critical — this directive is not covered by `default-src`.

**`base-uri`** — Restricts `<base>` tag href values. Not covered by `default-src`.

**`object-src 'none'`** — Disables Flash and other plugin content. Should be set explicitly.

**`upgrade-insecure-requests`** — Instructs the browser to upgrade HTTP resource requests to HTTPS.

The `unsafe-inline` Problem

The most common CSP mistake is including `'unsafe-inline'` in `script-src`:

``` Content-Security-Policy: script-src 'self' 'unsafe-inline' ```

This completely defeats CSP's XSS protection. An attacker who can inject `<script>alert(1)</script>` will have it executed — the policy allows it. Yet `'unsafe-inline'` is widespread because applications frequently use inline event handlers (`onclick="..."`), inline `<script>` blocks, or JavaScript in `href="javascript:..."` attributes.

The correct approach is to move inline scripts to external files and remove inline event handlers. Where this is genuinely impractical, CSP nonces provide a middle ground.

Nonces: Safe Inline Scripts

A nonce (number used once) is a random value generated per-request, placed in the CSP header and in script tags that are allowed to execute:

**Server-side (generating the nonce):** ```python import secrets nonce = secrets.token_urlsafe(16) response.headers["Content-Security-Policy"] = f"script-src 'self' 'nonce-{nonce}'" ```

**HTML:** ```html <script nonce="ABC123xyz..."> // This specific inline script is allowed initApp(); </script> ```

An attacker cannot predict the nonce — it changes with every response — so injected scripts cannot carry a valid nonce and are blocked. This allows legitimate inline scripts while maintaining XSS protection.

Hashes work similarly: you compute a SHA-256 hash of the script content and include it in the policy. Useful for static inline scripts that do not change.

`unsafe-eval` and Why It Matters

`'unsafe-eval'` permits `eval()`, `new Function()`, `setTimeout` with string arguments, and `setInterval` with string arguments. These features can execute arbitrary JavaScript from strings. If an attacker can control the string passed to `eval()`, they have code execution regardless of other CSP restrictions.

Avoid `'unsafe-eval'`. Applications that rely on it for legitimate functionality (template engines, certain frameworks) should use `strict-dynamic` instead, which is compatible with nonces.

Bypasses and Common Weaknesses

Allowlisted CDNs with user content

``` Content-Security-Policy: script-src 'self' https://cdnjs.cloudflare.com ```

cdnjs.cloudflare.com hosts thousands of JavaScript files, including older versions of libraries with known XSS payloads. An attacker can load a malicious library directly from the allowed CDN. The correct approach is to list specific file paths and use subresource integrity (SRI).

Wildcard subdomains

``` Content-Security-Policy: script-src 'self' *.example.com ```

If an attacker can control any subdomain of example.com (via subdomain takeover, for example), they can serve malicious scripts from the allowed origin.

`'strict-dynamic'` without nonces

```strict-dynamic` allows scripts loaded by trusted scripts to load additional scripts dynamically. Without nonces, this can be abused if any trusted script makes network requests controllable by the attacker.

Dangerous object-src

Omitting `object-src` means it inherits from `default-src`. If `default-src` is restrictive but `object-src` is not explicitly set to `'none'`, some older browsers allow plugin content that can bypass CSP entirely.

Missing form-action

`default-src` does not cover `form-action`. A policy without explicit `form-action` allows forms to submit to any destination — useful to an attacker for CSRF-adjacent data theft.

Report-Only Mode

CSP supports a report-only mode that logs violations without enforcing them:

``` Content-Security-Policy-Report-Only: default-src 'self'; report-uri /csp-report ```

This is invaluable for deploying a new CSP on an existing application without breaking functionality. You deploy in report-only mode, monitor the violation reports for legitimate resources being blocked, adjust the policy, and then switch to enforcement mode. Skipping this step and going straight to enforcement frequently breaks production applications.

Testing Your CSP

**CSP Evaluator** (Google) — Analyses a CSP string for weaknesses and bypass potential.

**Report-URI** — Receives violation reports from browsers, providing visibility into both legitimate mismatches and attack attempts.

**Security scanner** — Automated penetration testing tools check whether a CSP is present, whether it is in enforcement mode, whether `'unsafe-inline'` or `'unsafe-eval'` are present, and whether common bypass scenarios apply.

How Yrzo AI Evaluates CSP

Yrzo AI checks for the presence of a `Content-Security-Policy` header, verifies it is in enforcement mode rather than report-only, and analyses the policy directives for dangerous values (`'unsafe-inline'`, `'unsafe-eval'`, wildcards, dangerous allowlisted origins). A missing or ineffective CSP is flagged alongside any XSS vulnerabilities found — because a site with XSS and no CSP has a clean path from injection to account takeover, while a well-configured CSP degrades that path significantly.

[Scan your application at yrzoai.dev](https://yrzoai.dev) — results in 24 hours, no installation required.

Find out if your website has these vulnerabilities

Yrzo AI runs 44 automated security checks and delivers a full report in under 20 minutes. Starting from £399.

Scan your website →