Chapter 66: Content Security Policy (CSP)

Core Source Directives

The taxonomy of CSP fetch directives, the `default-src` fallback tree, navigation controls, and resource fetch interception.

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.
🎬 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 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:

  1. 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."
  2. 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.
  3. 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 .woff2 files from gstatic.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. Fetching api.coindesk.com succeeds, while fetching api.github.com triggers ERR_BLOCKED_BY_CSP.
  • Lines 100–108: Testing img-src. Loading an image from Unsplash succeeds, while placehold.co is 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.

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: Build a Complete Enterprise Directive Policy

You are tasked with writing a strict CSP policy for a fintech web application.

Requirements:

  1. Default fallback: Same origin only ('self').
  2. Scripts: Only 'self' and the payment gateway https://js.braintreegateway.com.
  3. Stylesheets: Only 'self' and https://cdn.jsdelivr.net.
  4. Fonts: Only 'self' and data URIs (data:).
  5. Images: Only 'self', https://assets.mycompany.com, and data: URIs.
  6. API Connections (connect-src): Only 'self', https://api.mycompany.com, and https://payments.braintree-api.com.
  7. Framing (frame-src): Only https://assets.braintreegateway.com.
  8. Plugin Objects (object-src): Completely disabled ('none').
  9. Base URI (base-uri): Locked to 'self'.
  10. Form Submissions (form-action): Locked to 'self'.

🏁 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. Assuming default-src Covers base-uri and form-action: Many developers assume default-src 'self' prevents an attacker from injecting <form action="https://phishing.com">. As specified in W3C CSP Level 3, form-action and base-uri do not fall back to default-src. If omitted, they default to allowing everything (*).
  2. Forgetting Google Fonts Host Separation: Google Fonts serves its CSS from https://fonts.googleapis.com, but the actual font binaries (.woff2) are hosted on https://fonts.gstatic.com. If you specify style-src https://fonts.googleapis.com but omit font-src https://fonts.gstatic.com, fonts will fail to load.
  3. Using Deprecated child-src Instead of frame-src and worker-src: In CSP Level 2, child-src controlled both frames and web workers. CSP Level 3 split these into frame-src (for iframes) and worker-src (for Web Workers / Service Workers).

💡 Pro Tips

  1. Always Set object-src 'none' Explicitly: Even if default-src 'none' is present, explicitly including object-src 'none' communicates intent and ensures that future changes to default-src do not inadvertently open legacy plugin vulnerabilities.
  2. Use connect-src to Block DNS Rebinding and C2 Exfiltration: Restricting connect-src is 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-src acts 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, and frame-ancestors DO NOT fall back to default-src; they must be declared explicitly.
  • CSP Level 3 introduces granular directives: script-src-elem / script-src-attr and style-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.
  • --
⭐ LEARN: HTML 🌟 ⚔️ QUIZ BATTLE ARENA // ACTIVE
3x
STREAK!
BONUS ACTIVE
COMBO
? Question 1 / 3

If a policy defines default-src 'self'; style-src https://cdn.com; and your HTML includes <img src="https://images.com/photo.jpg">, what does the browser do?

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

Which of the following directives DOES NOT inherit from default-src if left unspecified?

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

Why must both https://fonts.googleapis.com and https://fonts.gstatic.com be whitelisted when integrating Google Fonts?

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