LEARNING OBJECTIVES ⌵
- Master the complete taxonomy of CSP fetch directives (
default-src,script-src,style-src,img-src,connect-src,font-src,media-src,frame-src,worker-src,manifest-src,object-src). - Understand the exact inheritance and fallback tree governed by
default-src. - Identify the critical non-fallback directives (
base-uri,form-action,frame-ancestors) and their security boundaries. - Architect a multi-tiered CSP policy that restricts each subresource category to its minimum necessary privilege.
📖 The Mental Model & Story (Intuitive Foundation)
Imagine managing an ultra-secure research laboratory with multiple specialized departments: Chemistry, Robotics, Pharmaceuticals, IT Infrastructure, and the Mailroom.
Rather than giving every contractor a master pass to every room, you establish a departmental security roster:
- The Default Gate (
default-src): The general baseline rule. Unless a specific room has its own unique security policy, all visitors must follow the default rule: "Only badge-carrying employees may enter." - The High-Risk Rooms (
script-src,connect-src,object-src): Because dangerous chemicals or critical servers reside here, these rooms have strict overrides. Even if the general building permits trusted visitors, the Server Room (script-src) permits only specifically named senior engineers. - The Uncovered Zones (
base-uri,form-action,frame-ancestors): Certain areas—like the building's structural foundation (base-uri) or the outbound courier postal chute (form-action)—are NOT covered by the general badge rule. If you forget to post specific security guards at the mail chute, anyone can drop envelopes addressed to unauthorized overseas destinations.
[ default-src 'self' ] <=== Master Fallback Baseline
|
+-------------------------+-------------------------+
| | |
v v v
[ script-src ] [ style-src ] [ img-src ]
(Overrides default) (Overrides default) (Overrides default)
| | |
+-----+-----+ | |
| | | |
v v v v
script-src-elem script-src-attr style-src-elem/attr [ font-src / media-src / connect-src ]
========================================================================
⚠️ INDEPENDENT DIRECTIVES (DO NOT INHERIT FROM default-src):
- base-uri (Defends <base href="..."> resolution)
- form-action (Defends <form action="..."> submissions)
- frame-ancestors (Defends <iframe> embedding & Clickjacking)
========================================================================
Understanding how directives inherit from default-src—and critically, which directives do not inherit—is the foundation of authoring secure, maintainable CSPs.
Technical Deep Dive & Specifications
1. The Fetch Directives Taxonomy
Fetch directives control the locations from which specific resource types may be loaded and parsed by the browser:
| Directive | Affected HTML Elements & JavaScript APIs | Fallback to default-src? |
|---|---|---|
default-src |
Master fallback for all fetch directives if an explicit directive is absent. | N/A (Root) |
script-src |
<script src="...">, <script>...</script>, inline event handlers, eval(), new Function(). |
✅ YES |
style-src |
<link rel="stylesheet">, <style>, style="..." inline attributes. |
✅ YES |
img-src |
<img>, <picture>, <source>, image() in CSS, SVG <image>, favicon <link rel="icon">. |
✅ YES |
connect-src |
fetch(), XMLHttpRequest, WebSocket, EventSource, navigator.sendBeacon(). |
✅ YES |
font-src |
@font-face CSS rules, <link rel="preload" as="font">. |
✅ YES |
media-src |
<audio>, <video>, <track> (captions/subtitles). |
✅ YES |
frame-src |
<iframe>, <frame>, embedded browsing contexts. (Supersedes deprecated child-src). |
✅ YES |
worker-src |
new Worker(), new SharedWorker(), navigator.serviceWorker.register(). |
✅ YES |
manifest-src |
<link rel="manifest"> (PWA Web App Manifests). |
✅ YES |
object-src |
<object>, <embed>, <applet> (Legacy plugins, Flash, PDF viewers). |
✅ YES |
2. Independent Directives (The "Never-Fallback" Trio)
A common vulnerability in production CSPs is assuming that default-src 'self' protects all aspects of the document. It does not. The following directives operate completely independently of default-src:
+-----------------------------------------------------------------------------------------------+
| NON-FALLBACK DIRECTIVES (CRITICAL SECURITY HOLES) |
+-------------------+----------------------------------------------------+----------------------+
| Directive | Target Vulnerability | Consequence if Omitted |
+-------------------+----------------------------------------------------+----------------------+
| `base-uri` | Base URL Hijacking (`<base href="https://evil.com">`)| Attacker redirects all|
| | which alters resolution of relative links/scripts. | relative URLs! |
+-------------------+----------------------------------------------------+----------------------+
| `form-action` | Form Action Hijacking (`<form action="https://evil">`| Attacker steals credentials|
| | altering where sensitive POST payloads submit. | on form submit! |
+-------------------+----------------------------------------------------+----------------------+
| `frame-ancestors` | UI Redress / Clickjacking (`<iframe src="victim">`)| Any malicious origin |
| | embedding victim page in hidden iframe overlays. | can frame your site! |
+-------------------+----------------------------------------------------+----------------------+
3. Granular Level 3 Fetch Directives: Elements vs Attributes
CSP Level 3 introduced split sub-directives allowing fine-grained control over inline elements versus attributes:
[ script-src ]
├── [ script-src-elem ] : Controls <script> tags and <link rel="preload" as="script">
└── [ script-src-attr ] : Controls inline event handlers (onclick="...", onload="...")
[ style-src ]
├── [ style-src-elem ] : Controls <style> tags and <link rel="stylesheet">
└── [ style-src-attr ] : Controls inline style attributes (style="color: red;")
If script-src-elem is specified, it overrides script-src specifically for <script> elements. If absent, it falls back to script-src, which in turn falls back to default-src.
💻 Interactive Code Playground
Starter Code
Below is a full interactive demonstration showing how distinct directives govern different resource types. The policy restricts scripts to 'self', fonts to Google Fonts, images to Unsplash, and explicitly sets object-src 'none', base-uri 'self', and form-action 'self'.
Line-by-Line Code Breakdown
- Lines 8–18: The meta tag defines explicit directives:
default-src 'self': Any unmentioned directive defaults to same-origin.style-src 'self' 'unsafe-inline' https://fonts.googleapis.com: Permits Google Fonts CSS definitions.font-src https://fonts.gstatic.com: Google Fonts CSS loads raw.woff2files fromgstatic.com; this directive permits them.connect-src 'self' https://api.coindesk.com: Permits AJAX calls to CoinDesk while prohibiting all other external APIs.frame-src https://www.youtube-nocookie.com: Allows embedding YouTube video frames while disallowing any other video provider.base-uri 'self'&form-action 'self': Explicitly secures non-fallback attack surfaces.
- Lines 84–97: Testing
connect-src. Fetchingapi.coindesk.comsucceeds, while fetchingapi.github.comtriggersERR_BLOCKED_BY_CSP. - Lines 100–108: Testing
img-src. Loading an image from Unsplash succeeds, whileplacehold.cois dropped by the browser engine. - Lines 111–120: Testing
frame-src. Embedding YouTube embeds correctly, whereas Vimeo is blocked from framing.
Expected Browser Render Output
- The web page renders with modern monospace styling loaded from Google Fonts (
Fira Code). - CoinDesk API fetch displays real-time Bitcoin pricing in the green output box.
- GitHub API fetch fails immediately with a CSP console error.
- Unsplash image renders cleanly; placeholder image renders as a broken image icon with a console violation.
- YouTube embed plays seamlessly; Vimeo embed displays a gray browser block frame with a CSP violation warning.
🏋️ Hands-On Exercise
🎯 The Challenge: Build a Complete Enterprise Directive Policy
You are tasked with writing a strict CSP policy for a fintech web application.
Requirements:
- Default fallback: Same origin only (
'self'). - Scripts: Only
'self'and the payment gatewayhttps://js.braintreegateway.com. - Stylesheets: Only
'self'andhttps://cdn.jsdelivr.net. - Fonts: Only
'self'and data URIs (data:). - Images: Only
'self',https://assets.mycompany.com, anddata:URIs. - API Connections (
connect-src): Only'self',https://api.mycompany.com, andhttps://payments.braintree-api.com. - Framing (
frame-src): Onlyhttps://assets.braintreegateway.com. - Plugin Objects (
object-src): Completely disabled ('none'). - Base URI (
base-uri): Locked to'self'. - Form Submissions (
form-action): Locked to'self'.
🏁 Starter Code Sandbox
⚠️ Common Pitfalls
- Assuming
default-srcCoversbase-uriandform-action: Many developers assumedefault-src 'self'prevents an attacker from injecting<form action="https://phishing.com">. As specified in W3C CSP Level 3,form-actionandbase-urido not fall back todefault-src. If omitted, they default to allowing everything (*). - Forgetting Google Fonts Host Separation: Google Fonts serves its CSS from
https://fonts.googleapis.com, but the actual font binaries (.woff2) are hosted onhttps://fonts.gstatic.com. If you specifystyle-src https://fonts.googleapis.combut omitfont-src https://fonts.gstatic.com, fonts will fail to load. - Using Deprecated
child-srcInstead offrame-srcandworker-src: In CSP Level 2,child-srccontrolled both frames and web workers. CSP Level 3 split these intoframe-src(for iframes) andworker-src(for Web Workers / Service Workers).
💡 Pro Tips
- Always Set
object-src 'none'Explicitly: Even ifdefault-src 'none'is present, explicitly includingobject-src 'none'communicates intent and ensures that future changes todefault-srcdo not inadvertently open legacy plugin vulnerabilities. - Use
connect-srcto Block DNS Rebinding and C2 Exfiltration: Restrictingconnect-srcis your last line of defense against supply-chain attacks (e.g., a rogue npm package) attempting to transmit stolen passwords or session tokens to an external command-and-control server.
📌 Key Takeaways
default-srcacts as the master fallback for most fetch directives (script-src,style-src,img-src,connect-src,font-src,media-src,frame-src,worker-src,manifest-src,object-src).base-uri,form-action, andframe-ancestorsDO NOT fall back todefault-src; they must be declared explicitly.- CSP Level 3 introduces granular directives:
script-src-elem/script-src-attrandstyle-src-elem/style-src-attr. - Multi-origin services (like Google Fonts or payment gateways) often require distinct origins for stylesheets (
googleapis.com) and font binaries (gstatic.com). - Explicitly disabling legacy plugin execution with
object-src 'none'is an industry-standard best practice. - --