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'.
📖 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 literallyhttp://selforhttps://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/datamatches'self'(Same scheme, host, port).http://app.example.com/api/datadoes NOT match (Scheme mismatch: HTTP vs HTTPS).https://api.example.com/datadoes NOT match (Host mismatch: subdomain difference).https://app.example.com:8080/datadoes 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'toscript-srcneutralizes 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 anEvalError: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 anEvalError.
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.
🏋️ 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:
- The developer forgot quotes on
selfandnone. - The developer allowed
script-src *to make a tracking script work, disabling all XSS protection. - The developer added
data:toscript-src, opening an XSS injection path via base64 payload strings. - 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:
default-srcis strictly'self'.script-srcallows'self', the analytics script fromhttps://analytics.trusted.com, and WebAssembly execution via'wasm-unsafe-eval'(no traditionaleval()!).object-srcis strictly'none'.img-srcallows'self'andhttps://images.trusted.com(no arbitrarydata:scripts!).
🏁 Starter Code Sandbox
⚠️ Common Pitfalls
- Omitting Single Quotes on Keywords (
selfvs'self'): Writingscript-src selfis one of the most widespread typos in web security. The browser treatsselfas a hostname, blocking your own website's scripts while trying to fetch fromhttp://self. - Allowing
data:inscript-src: Whiledata:is common inimg-srcfor inline icons, puttingdata:inscript-srcpermits an attacker to bypass all anti-XSS protections via<script src="data:text/javascript,alert(document.cookie)"></script>. - 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
- 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. - Use
'report-sample'in Script Policies: Always append'report-sample'toscript-srcandstyle-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:andblob:should be avoided inscript-srcbecause they allow arbitrary JavaScript payload execution. - CSP Level 3 provides
'wasm-unsafe-eval'to allow WebAssembly compilation without exposing the application to JavaScripteval()vulnerabilities. - --