Chapter 66: Content Security Policy (CSP)

CSP Source Values & Keywords

The grammar of CSP expressions: keywords, quoting semantics, host wildcards, scheme sources, and dangerous bypasses.

LEARNING OBJECTIVES
  • Distinguish between CSP source expressions: keyword sources, scheme sources, host sources, and nonce/hash tokens.
  • Understand why single quotes are mandatory for CSP keywords ('self', 'none', 'unsafe-inline', 'unsafe-eval') and the disastrous security failure of omitting them.
  • Analyze the security risks of 'unsafe-inline', 'unsafe-eval', data:, and broad host wildcards (*, *.example.com).
  • Explore modern specialty keywords including 'wasm-unsafe-eval', 'strict-dynamic', and 'report-sample'.
🎬 INTERACTIVE VISUAL PIPELINE Core Architecture Simulation
🌐
1. Input
Directives & Tags
⚙️
2. Parse
Tokenizer & AST
🌳
3. Layout
Box Model & Flow
🎨
4. Render
GPU Paint & Composite
PHASE 1: INPUT & DIRECTIVES
Browser receives declarative markup stream, parsing tag tokens and initializing component state.

📖 The Mental Model & Story (Intuitive Foundation)

Imagine writing instructions for a courthouse security guard:

  • Rule 1: "Admit only the judge herself."
  • Rule 2: "Admit only a person whose legal first name is Self."

In the English language, a subtle distinction in punctuation or grammar completely inverts the meaning. In Content Security Policy grammar:

  • 'self' (with single quotes) is a keyword that instructs the browser: "Permit resources only from the exact origin (same protocol, host, and port) of the document currently being viewed."
  • self (without single quotes) is a host source that instructs the browser: "Permit resources from a remote web server on the local network whose DNS domain name is literally http://self or https://self."
+-----------------------------------------------------------------------------------------------+
|                               THE CATASTROPHIC QUOTING BUG                                    |
+-----------------------------------------------------------------------------------------------+
| INTENDED POLICY:   Content-Security-Policy: script-src 'self';                                |
| PARSER SEES:       Keyword 'self' ===> Match document origin (e.g. https://bank.com)          |
| RESULT:            ✅ First-party scripts allowed. Injected attacker scripts BLOCKED.         |
|                                                                                               |
| ACCIDENTAL TYPO:   Content-Security-Policy: script-src self;                                  |
| PARSER SEES:       Host name "self" ===> Match http://self/* or https://self/*                |
| RESULT:            ❌ Origin https://bank.com is BLOCKED!                                    |
|                    ❌ Attacker registers domain/hostname "self" on LAN and executes code!   |
+-----------------------------------------------------------------------------------------------+

The W3C CSP grammar enforces strict syntactical rules. Mastering which tokens require quotes, which require colons, and which permit wildcards is vital to preventing subtle configuration vulnerabilities.


Technical Deep Dive & Specifications

1. The Taxonomy of CSP Source Expressions

A CSP directive is composed of a directive name followed by a whitespace-delimited list of Source Expressions:

directive-name  source-expression-1  source-expression-2  ... ;
Source Expression Type Syntax Pattern Examples Purpose / Semantics
Special Keywords Quoted with single quotes ('...') 'self', 'none', 'unsafe-inline', 'unsafe-eval', 'wasm-unsafe-eval', 'report-sample' Instructs the CSP state machine to apply built-in algorithmic checks.
Host Sources Domain name or IP address, optional port and path example.com, *.example.com, https://cdn.example.com:8443/dist/ Restricts fetches to specific network hostnames, subdomains, ports, or paths.
Scheme Sources Scheme name followed by a colon (:) https:, data:, blob:, mediastream:, filesystem: Restricts fetches to specific URI scheme protocols.
Cryptographic Primitives Quoted algorithm prefix + base64 payload 'nonce-4Ae8bF2...', 'sha256-abc123...' Validates element attributes or computes SHA cryptographic digests.

2. Deep Dive into CSP Keywords

'self'

Matches the exact origin of the current document: $$\text{Origin} = (\text{Scheme}, \text{Host}, \text{Port})$$ If your document is hosted at https://app.example.com:443:

  • https://app.example.com/api/data matches 'self' (Same scheme, host, port).
  • http://app.example.com/api/data does NOT match (Scheme mismatch: HTTP vs HTTPS).
  • https://api.example.com/data does NOT match (Host mismatch: subdomain difference).
  • https://app.example.com:8080/data does NOT match (Port mismatch).

'none'

Matches nothing. When placed in a directive (e.g., object-src 'none', default-src 'none'), it completely disallows that entire category of resources. It must be the only source expression in the directive.

'unsafe-inline'

Allows the execution of inline <script> tags, inline <style> tags, and inline event handlers (onclick="...", onload="...", style="...").

⚠️ CRITICAL VULNERABILITY: Adding 'unsafe-inline' to script-src neutralizes CSP's primary defense against XSS. If an attacker injects <script>stealData()</script>, the browser will execute it.

'unsafe-eval'

Allows the evaluation of string-to-code dynamic JavaScript APIs:

  • eval("console.log(1)")
  • window.Function("a", "b", "return a + b")
  • setTimeout("alert(1)", 1000) (passing string instead of function)
  • setInterval("doWork()", 1000)

'wasm-unsafe-eval' (CSP Level 3)

Allows compiling and instantiating WebAssembly modules (WebAssembly.compile(), WebAssembly.instantiate()) without opening the door to traditional JavaScript eval(). This is essential for high-performance apps (gaming, CAD, media editing) running WebAssembly.

'report-sample' (CSP Level 3)

Instructs the browser to include a truncated 40-character snippet of the violating code sample directly inside the JSON violation telemetry payload sent to the reporting server. This provides immediate debugging context during security incidents.

+-----------------------------------------------------------------------------------------------+
|                                  DANGEROUS SOURCE COMBINATIONS                                |
+-------------------------------+---------------------------------------------------------------+
| Source Expression             | Security Consequence                                          |
+-------------------------------+---------------------------------------------------------------+
| `script-src *`                | Complete bypass: Attacker loads scripts from anywhere.        |
| `script-src 'unsafe-inline'`  | Complete bypass: Injected inline `<script>` blocks execute.   |
| `script-src https:`           | Bypass: Attacker hosts script on any valid HTTPS endpoint.    |
| `script-src data:`            | Bypass: Attacker injects `<script src="data:text/javascript,...">` |
| `script-src *.googleapis.com` | Bypass: Google APIs host JSONP endpoints that can execute code.|
+-------------------------------+---------------------------------------------------------------+

💻 Interactive Code Playground

Starter Code

The following interactive playground demonstrates the difference between valid 'self' execution and the effects of 'unsafe-eval'. It demonstrates how the browser blocks string-to-code evaluation when 'unsafe-eval' is omitted.

Line-by-Line Code Breakdown

  • Lines 8–13: The CSP meta tag specifies script-src 'self' 'unsafe-inline' 'report-sample'. Notice that 'unsafe-eval' is deliberately absent.
  • Lines 73–76: The "Execute Safe Function" button runs standard compiled JavaScript (Math.sqrt(144)), which evaluates without hindrance.
  • Lines 79–88: The "Execute eval" button invokes eval("2 + 2"). The V8/SpiderMonkey runtime queries the active CSP context, detects the absence of 'unsafe-eval', halts execution, and raises an EvalError:
    • EvalError: Refused to evaluate a string as JavaScript because 'unsafe-eval' is not an allowed source of script in the following Content Security Policy directive...
  • Lines 91–100: The "Execute new Function" button attempts to compile code via new Function(...). This is also intercepted and blocked by the engine with an EvalError.

Expected Browser Render Output

  • Clicking Execute Safe Function updates the log: ✅ Safe Execution Succeeded: Math.sqrt(144) = 12.
  • Clicking Execute eval("2 + 2") immediately catches an exception and prints: 🛑 BLOCKED BY CSP! Error: Refused to evaluate a string as JavaScript because 'unsafe-eval' is not an allowed source....
  • Clicking Execute new Function("return 42") yields the same EvalError.

SYS: ACTIVE
HULL: 98%
CORE: STABLE
NET: ONLINE
HTML STARSHIP CODE TERMINAL playground.html
LIVE RENDER & DIAGNOSTICS CORE TEMP: 45°C
INSPECTING DOM: VALID
TAGS: SCANNING...

🏋️ Hands-On Exercise

🎯 The Challenge: Fix the Broken & Vulnerable CSP

A junior developer configured a CSP for an e-commerce checkout portal. However, the configuration is broken and insecure:

  1. The developer forgot quotes on self and none.
  2. The developer allowed script-src * to make a tracking script work, disabling all XSS protection.
  3. The developer added data: to script-src, opening an XSS injection path via base64 payload strings.
  4. The page needs WebAssembly for a cryptography signature module, but the developer added 'unsafe-eval' instead of the modern restricted 'wasm-unsafe-eval'.

Your Mission: Refactor the policy so that:

  1. default-src is strictly 'self'.
  2. script-src allows 'self', the analytics script from https://analytics.trusted.com, and WebAssembly execution via 'wasm-unsafe-eval' (no traditional eval()!).
  3. object-src is strictly 'none'.
  4. img-src allows 'self' and https://images.trusted.com (no arbitrary data: scripts!).

🏁 Starter Code Sandbox

SYS: ACTIVE
HULL: 98%
CORE: STABLE
NET: ONLINE
STARTER CODE SANDBOX exercise.html
LIVE RENDER & DIAGNOSTICS CORE TEMP: 45°C
INSPECTING DOM: VALID
TAGS: SCANNING...

⚠️ Common Pitfalls

  1. Omitting Single Quotes on Keywords (self vs 'self'): Writing script-src self is one of the most widespread typos in web security. The browser treats self as a hostname, blocking your own website's scripts while trying to fetch from http://self.
  2. Allowing data: in script-src: While data: is common in img-src for inline icons, putting data: in script-src permits an attacker to bypass all anti-XSS protections via <script src="data:text/javascript,alert(document.cookie)"></script>.
  3. Using Broad Subdomain Wildcards (e.g. *.cloudfront.net): Wildcarding shared multi-tenant hosting platforms allows an attacker to host malicious payloads on their own AWS CloudFront or Firebase distribution and execute them within your origin.

💡 Pro Tips

  1. Adopt 'wasm-unsafe-eval' for Modern Runtimes: If your application uses WebAssembly (e.g., SQLite in the browser, FFmpeg.wasm, image processing), never enable 'unsafe-eval'. Use 'wasm-unsafe-eval' to isolate WASM compilation from JavaScript execution.
  2. Use 'report-sample' in Script Policies: Always append 'report-sample' to script-src and style-src. When a policy violation occurs, the browser reports the first 40 characters of the offending script to your reporting endpoint, making forensic triage significantly faster.

📌 Key Takeaways

  • CSP keywords ('self', 'none', 'unsafe-inline', 'unsafe-eval', 'wasm-unsafe-eval', 'report-sample') MUST be enclosed in single quotes.
  • Omitting quotes causes the parser to treat the keyword as a hostname, resulting in critical security lapses and broken sites.
  • 'unsafe-inline' completely disables CSP's protection against injected inline scripts and event attributes.
  • Scheme sources like data: and blob: should be avoided in script-src because they allow arbitrary JavaScript payload execution.
  • CSP Level 3 provides 'wasm-unsafe-eval' to allow WebAssembly compilation without exposing the application to JavaScript eval() vulnerabilities.
  • --
⭐ LEARN: HTML 🌟 ⚔️ QUIZ BATTLE ARENA // ACTIVE
3x
STREAK!
BONUS ACTIVE
COMBO
? Question 1 / 3

What happens if an engineer writes default-src none; instead of default-src 'none';?

Question 1 / 3 Topic: HTML Fundamentals
00:45 REMAINING
XP REWARD
+250 XP
3x
STREAK!
BONUS ACTIVE
COMBO
? Question 2 / 3

Why should data: never be included in the script-src directive?

Question 2 / 3 Topic: HTML Fundamentals
00:45 REMAINING
XP REWARD
+250 XP
3x
STREAK!
BONUS ACTIVE
COMBO
? Question 3 / 3

How does 'wasm-unsafe-eval' improve security over 'unsafe-eval' for applications using WebAssembly?

Question 3 / 3 Topic: HTML Fundamentals
00:45 REMAINING
XP REWARD
+250 XP