Chapter 69: Subresource Integrity (SRI) & Referrer Policy

SRI and CORS Requirements

Why Subresource Integrity mandates Cross-Origin Resource Sharing (CORS), how the opaque response barrier works, and how this prevents cryptographic oracle side-channel attacks.

LEARNING OBJECTIVES
  • Understand why cross-origin resources with integrity require the crossorigin attribute.
  • Explain the concept of "opaque responses" under the Same-Origin Policy.
  • Demystify the cryptographic side-channel probing attack that made the CORS requirement mandatory.
  • Correctly configure CDN response headers (Access-Control-Allow-Origin, Vary: Origin) to avoid caching bugs.
🎬 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 a customs inspection checkpoint at an international border.

If a diplomatic courier carries a sealed diplomatic pouch, international law strictly forbids customs officials from opening the pouch to inspect the contents. If the customs officer is handed a document that says "Verify that the exact text inside the diplomatic pouch matches this secret fingerprint," the officer has to refuse. To verify the fingerprint, the officer would have to read every byte inside the pouch—which is illegal without explicit diplomatic authorization (a waiver).

+-----------------------------------------------------------------------------------------+
|                                THE DIPLOMATIC POUCH ANALOGY                             |
+-----------------------------------------------------------------------------------------+
|                                                                                         |
|   Without CORS Permission (No-CORS / Opaque):                                           |
|   +-------------------+       +-----------------------+       +-----------------------+ |
|   | Origin: App.com   | ----> | Cross-Origin Server   | ----> | Opaque Response       | |
|   | <script integrity>|       | (Private User Data)   |       | Browser cannot read   | |
|   +-------------------+       +-----------------------+       | the raw bytes to hash | |
|                                                               +-----------------------+ |
|                                                                           |             |
|                                                                           v             |
|                                                                  🛑 BLOCKED BY BROWSER   |
|                                                                                         |
|   With CORS Permission (crossorigin="anonymous" + Access-Control-Allow-Origin):        |
|   +-------------------+       +-----------------------+       +-----------------------+ |
|   | Origin: App.com   | ----> | CDN Server with       | ----> | CORS-Exposed Response | |
|   | <script           |       | Access-Control-Allow- |       | Browser reads bytes,  | |
|   | crossorigin="..." |       | Origin: *             |       | computes SHA-384 hash | |
|   | integrity="...">  |       +-----------------------+       +-----------------------+ |
|   +-------------------+                                                   |             |
|                                                                           v             |
|                                                                  ✅ EXECUTES IF HASH OK  |
+-----------------------------------------------------------------------------------------+

In browser architecture, cross-origin resources fetched without CORS are treated as opaque. The browser is allowed to execute the JavaScript, but it is strictly forbidden from inspecting the raw bytes. Because calculating a SHA hash requires inspecting every single byte, the browser must have CORS permission before performing Subresource Integrity validation.


Technical Deep Dive & Specifications

The Vulnerability: The Cryptographic Oracle Attack

Why did browser vendors and the W3C mandate that SRI fails without CORS?

Imagine an attacker hosting https://evil-attacker.com. Suppose a user visits the attacker's site while logged into their online bank (https://mybank.com).

The bank has a sensitive JSONP or JavaScript endpoint: https://mybank.com/api/account-details.js which renders:

var account = { balance: "$14,500.00", user: "Alice" };

Under normal Same-Origin Policy rules, evil-attacker.com can load this script via <script src="https://mybank.com/api/account-details.js">, but the attacker cannot directly read the source code text.

Now imagine if the browser allowed SRI without CORS:

  1. evil-attacker.com guesses possible account balance strings ("$10,000.00", "$10,001.00", etc.).
  2. The attacker calculates the SHA-384 hash for each guess.
  3. The attacker dynamically creates <script src="https://mybank.com/api/account-details.js" integrity="sha384-[GUESS_HASH]">.
  4. If the guess is wrong, the browser throws an onerror event.
  5. If the guess is correct, the browser fires onload!
+------------------------------------------------------------------------------------+
|                HOW SRI WITHOUT CORS WOULD BECOME A DATA-THEFT ORACLE               |
+------------------------------------------------------------------------------------+
|                                                                                    |
|  Attacker guesses: "balance: $14,500" -> Hash: sha384-abc                          |
|  Injects: <script src="https://bank.com/api" integrity="sha384-abc">              |
|  Result: 🛑 onerror (Guess wrong)                                                  |
|                                                                                    |
|  Attacker guesses: "balance: $14,501" -> Hash: sha384-xyz                          |
|  Injects: <script src="https://bank.com/api" integrity="sha384-xyz">              |
|  Result: ✅ onload (MATCH FOUND! Attacker now knows the user's private balance!)    |
|                                                                                    |
+------------------------------------------------------------------------------------+

By turning SRI into a cryptographic oracle, an attacker could extract private user data, CSRF tokens, or personal identifiers across origins.

To eliminate this vulnerability, the W3C SRI specification mandates: If a resource is fetched from a different origin and includes an integrity attribute, the fetch mode must be CORS.


The crossorigin Attribute Values

To satisfy the CORS requirement on <script> and <link> elements, you must supply the crossorigin attribute:

Value Fetch Mode Credentials (Cookies/Auth) Sent? Server Response Header Required
(omitted) no-cors (Opaque) Yes None (Incompatible with SRI across origins)
"" or "anonymous" cors No (No cookies or client certs) Access-Control-Allow-Origin: * or Access-Control-Allow-Origin: <origin>
"use-credentials" cors Yes (Sends session cookies & auth headers) Access-Control-Allow-Origin: <origin> AND Access-Control-Allow-Credentials: true

📌 Best Practice: For static public CDNs (cdnjs, jsDelivr, unpkg, Google Fonts), always use crossorigin="anonymous".


CDN Server Header Requirements

For SRI to succeed, the CDN server hosting the script or stylesheet must respond with the appropriate CORS headers:

HTTP/2 200 OK
Content-Type: application/javascript; charset=utf-8
Access-Control-Allow-Origin: *
Vary: Origin, Accept-Encoding
Cache-Control: public, max-age=31536000, immutable

Why Vary: Origin is Critical on the CDN

If a CDN edge server caches a response for an asset requested without CORS (e.g. from a client that didn't pass Origin), and then serves that cached response (which lacks Access-Control-Allow-Origin) to a client performing an SRI CORS check, the SRI check will fail.

The Vary: Origin header instructs intermediate proxies and CDNs to cache separate copies based on the presence and value of the incoming Origin request header.


💻 Interactive Code Playground

Starter Code

Line-by-Line Code Breakdown

  • Lines 8–14 (<link rel="stylesheet"...): Loads Font Awesome icons from Cloudflare CDN. It includes both integrity and crossorigin="anonymous". Cloudflare responds with Access-Control-Allow-Origin: *, allowing the browser to read the stylesheet bytes and compute the SHA-512 digest.
  • Lines 32–36 (<script src="..." integrity="..." crossorigin="anonymous">): Loads Axios. Because crossorigin="anonymous" is declared, the browser sends the HTTP request with mode cors and excludes credentials. The downloaded bytes are hashed and compared with the sha512- string.
  • Lines 38–51 (<script>...): Confirms runtime execution. If crossorigin had been omitted, modern browsers would reject the fetch immediately with a NetworkError.

Expected Browser Render Output


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...
+-----------------------------------------------------------------------+
| 🛡️ Subresource Integrity + CORS Check                                 |
| This page demonstrates valid cross-origin asset loading with CORS.   |
|                                                                       |
| ✅ Axios v1.6.7 successfully loaded with CORS & SRI verification!     |
+-----------------------------------------------------------------------+

🏋️ Hands-On Exercise

🎯 The Challenge: Diagnose and Fix a Broken CDN Integration

Instructions:

  1. A junior engineer deployed the following snippet to production, but the script fails to execute, and the browser console displays: Access to script at 'https://cdn.partner.org/analytics.js' from origin 'https://mybank.com' has been blocked by CORS policy.
  2. Identify the two defects in the <script> tag configuration.
  3. Fix the tag so that it:
    • Requests the asset using CORS mode without transmitting session cookies.
    • Verifies the provided SHA-384 hash: sha384-9yX8w7V6u5T4s3R2q1P0o9N8m7L6k5J4i3H2g1F0e9D8c7B6a5Z4Y3X2W1V0U
    • Handles integrity verification errors gracefully by logging to the telemetry system.

🏁 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 crossorigin="anonymous" on Cross-Origin Scripts: When you add integrity to an external script without crossorigin, browsers refuse to compute the digest because the response is opaque, causing an immediate network failure.
  2. Using crossorigin="use-credentials" with Wildcard CORS: If you set crossorigin="use-credentials" on a CDN script, the CDN server cannot use Access-Control-Allow-Origin: *. Browsers strictly reject credentialed requests with wildcard origins. Public CDNs almost never support credentialed requests.
  3. Missing Vary: Origin on Custom CDNs: If your origin server hosts static assets via an AWS S3/CloudFront CDN, you must ensure CloudFront forwards the Origin header and caches on Vary: Origin. Otherwise, non-CORS requests may populate edge caches without CORS headers, breaking subsequent SRI requests.

💡 Pro Tips

  1. Same-Origin SRI Does Not Need crossorigin: If the script or stylesheet is hosted on the same origin (e.g., /static/bundle.js), the response is not opaque. You can declare integrity="sha384-..." without needing crossorigin="anonymous".
  2. Nginx CDN Configuration for SRI: When configuring Nginx or an edge proxy for static assets, always add:
    location ~* \.(js|css|woff2)$ {
        add_header Access-Control-Allow-Origin "*" always;
        add_header Vary "Origin, Accept-Encoding" always;
        expires 1y;
        add_header Cache-Control "public, immutable";
    }
    

📌 Key Takeaways

  • SRI requires the browser to inspect every byte of a resource to compute its cryptographic hash.
  • Cross-origin requests without CORS produce "opaque" responses, which browsers cannot read.
  • Allowing SRI on opaque responses would create a cryptographic oracle vulnerability, leaking sensitive cross-origin data.
  • Therefore, cross-origin resources with integrity must declare crossorigin="anonymous" (or crossorigin="use-credentials").
  • CDN servers must respond with Access-Control-Allow-Origin: * and Vary: Origin.
  • --
⭐ LEARN: HTML 🌟 ⚔️ QUIZ BATTLE ARENA // ACTIVE
3x
STREAK!
BONUS ACTIVE
COMBO
? Question 1 / 3

Why does the W3C SRI specification strictly prohibit checking hashes on cross-origin resources that lack CORS permission?

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

Which attribute must you add to <script src="https://cdn.example.com/app.js" integrity="..."> to enable CORS verification without sending user cookies?

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

If a script is hosted on the same origin as the HTML document (e.g., <script src="/assets/app.js" integrity="sha384-...">), is crossorigin="anonymous" required?

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