Topic: mcp server subresource integrity
MCP server subresource integrity — SRI for CDN-loaded scripts and styles
Subresource Integrity (SRI) is a browser security feature that lets you provide a cryptographic hash for an external resource (a JavaScript file or stylesheet loaded from a CDN). The browser computes the hash of the downloaded content and refuses to execute or apply it if the hash doesn't match. Without SRI, a compromised CDN can serve a modified version of a script that your MCP server's web interface loads — and that modified script runs with full page privileges. This page covers how CDN supply chain attacks work in practice, how to generate and add SRI hashes, and how to automate the process in a build pipeline.
How CDN supply chain attacks work
If an MCP server's web interface loads a script from a CDN — a React build, a utility library, an analytics snippet — the browser trusts and executes whatever the CDN serves. If an attacker compromises the CDN (account takeover, CI/CD injection, BGP hijacking, DNS poisoning), they can modify the script. The browser has no way to detect the change because the URL is still correct and the CDN's TLS certificate is still valid.
This is not a theoretical threat. Polyfill.io (a widely used CDN for browser compatibility scripts) was compromised in 2024 when its domain was sold; the new owner began serving malicious payloads to the millions of sites that loaded the script. Websites that had SRI hashes on their polyfill.io script tags were protected — the browser detected the hash mismatch and refused to execute the payload.
MCP server web interfaces typically serve a small number of pages (OAuth flow, setup wizard, admin panel), so the number of external scripts to protect is manageable. The fix is straightforward: add integrity and crossorigin attributes to external resource tags.
Four SRI patterns for MCP server web interfaces
1. Generating SRI hashes for known CDN resources
# Generate SRI hash for a CDN resource using curl + openssl
# Method 1: curl + openssl (works on Linux/macOS)
curl -sL https://cdn.jsdelivr.net/npm/alpinejs@3.14.1/dist/cdn.min.js \
| openssl dgst -sha384 -binary \
| openssl base64 -A
# Output: (base64 hash)
# Use as: integrity="sha384-{output}"
# Method 2: Using the srihash.org website
# Visit https://www.srihash.org/ and paste the CDN URL
# Method 3: Node.js script for build pipeline integration
node -e "
const crypto = require('crypto')
const https = require('https')
function generateSRI(url) {
return new Promise((resolve, reject) => {
https.get(url, (res) => {
const hash = crypto.createHash('sha384')
res.on('data', (chunk) => hash.update(chunk))
res.on('end', () => resolve('sha384-' + hash.digest('base64')))
res.on('error', reject)
})
})
}
generateSRI('https://cdn.jsdelivr.net/npm/alpinejs@3.14.1/dist/cdn.min.js')
.then(hash => console.log(hash))
"
2. Adding SRI attributes to HTML resource tags
<!-- VULNERABLE: external script with no integrity check -->
<script src="https://cdn.example.com/lib.min.js"></script>
<!-- SECURE: external script with SRI hash -->
<script
src="https://cdn.jsdelivr.net/npm/alpinejs@3.14.1/dist/cdn.min.js"
integrity="sha384-HASH_VALUE_HERE"
crossorigin="anonymous"
defer
></script>
<!-- VULNERABLE: external stylesheet with no integrity check -->
<link rel="stylesheet" href="https://cdn.example.com/styles.min.css">
<!-- SECURE: external stylesheet with SRI hash -->
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/@picocss/pico@2.0.6/css/pico.min.css"
integrity="sha384-HASH_VALUE_HERE"
crossorigin="anonymous"
>
<!-- Multiple hash algorithms (browser uses the strongest supported one): -->
<script
src="https://cdn.example.com/lib.min.js"
integrity="sha256-HASH256 sha384-HASH384"
crossorigin="anonymous"
></script>
<!-- The crossorigin="anonymous" attribute is REQUIRED:
- Without it, the browser loads the resource without CORS headers
- SRI verification requires the browser to read the response body
- Cross-origin reads require CORS — anonymous = no credentials sent
- The CDN must send Access-Control-Allow-Origin: * or matching origin -->
3. Automating SRI in a Node.js build pipeline
// build/generate-sri.js — run as part of your build pipeline
// Fetches CDN resources, computes SHA-384 hashes, injects into HTML templates
import crypto from 'crypto'
import https from 'https'
import fs from 'fs/promises'
// Define the CDN resources your HTML templates use
const CDN_RESOURCES = {
'alpinejs': {
url: 'https://cdn.jsdelivr.net/npm/alpinejs@3.14.1/dist/cdn.min.js',
placeholder: 'SRI_ALPINEJS',
},
'pico-css': {
url: 'https://cdn.jsdelivr.net/npm/@picocss/pico@2.0.6/css/pico.min.css',
placeholder: 'SRI_PICOCSS',
},
}
async function fetchHash(url: string): Promise {
return new Promise((resolve, reject) => {
https.get(url, (res) => {
const hash = crypto.createHash('sha384')
res.on('data', (chunk) => hash.update(chunk))
res.on('end', () => resolve('sha384-' + hash.digest('base64')))
res.on('error', reject)
}).on('error', reject)
})
}
async function main() {
const hashes: Record = {}
for (const [name, { url, placeholder }] of Object.entries(CDN_RESOURCES)) {
console.log(`Computing SRI for ${name}...`)
hashes[placeholder] = await fetchHash(url)
console.log(` ${placeholder}: ${hashes[placeholder]}`)
}
// Write hashes to a JSON file consumed by your template engine
await fs.writeFile('build/sri-hashes.json', JSON.stringify(hashes, null, 2))
console.log('SRI hashes written to build/sri-hashes.json')
}
main().catch(console.error)
// Then in your Express template:
// const sriHashes = JSON.parse(fs.readFileSync('build/sri-hashes.json'))
// In EJS: integrity="<%= sriHashes.SRI_ALPINEJS %>"
4. Self-hosting as the stronger alternative
// SRI protects against CDN compromise, but the strongest defense
// is eliminating the CDN dependency entirely
// Option A: Bundle at build time (webpack / vite / esbuild)
// npm packages are fetched from the npm registry once at build time
// The bundled output is served from your own origin
// No CDN in the request path at runtime — supply chain risk shifts to npm
// (address with package-lock.json + npm audit)
// Option B: Vendor the scripts (copy to /public/)
// Copy the CDN script once during development setup
// Commit to your repository
// Serve from your own server
// npm install --save-dev @picocss/pico
// cp node_modules/@picocss/pico/css/pico.min.css public/vendor/pico.min.css
// Load from your own origin — no CDN dependency at runtime
// <link rel="stylesheet" href="/vendor/pico.min.css">
// When to use SRI vs self-hosting:
// - SRI: you need the CDN for performance/caching and can't bundle
// - Self-hosting: you prefer zero CDN dependencies and control the full stack
// - For MCP server web UIs: prefer bundling or vendoring — the web UI is
// not performance-critical enough to require CDN edge caching
What SkillAudit checks
- External
<script src="...">tags withoutintegrityattribute — HIGH; CDN supply chain attack can replace the script with a malicious payload; browser cannot detect the change - External
<link rel="stylesheet" href="...">withoutintegrityattribute — WARN; malicious stylesheet can exfiltrate content via CSS attribute selectors and background-image requests integrityattribute present butcrossoriginmissing — WARN; browser may not perform SRI verification correctly without the CORS headers thatcrossorigin="anonymous"triggers- SRI hash uses SHA-1 or MD5 — HIGH; these algorithms are broken; SHA-384 is the minimum recommended hash length
- CDN resources loaded from
cdn.polyfill.ioor similar compromised-history CDNs — HIGH; specific CDNs with known compromise history; recommend self-hosting or switch to a CDN with a clean track record
Scope note: stdio-only servers
SRI is only relevant for MCP servers that serve HTML pages in a browser context. Pure stdio servers (no HTTP transport, no admin UI) load no browser scripts and have no SRI attack surface. SkillAudit's SRI checks apply only to servers with a detectable HTTP/browser-accessible component.
See also
- MCP server Content Security Policy — CSP script-src allowlist complements SRI by restricting which origins can load scripts
- MCP server supply chain security — npm dependency integrity and the broader supply chain picture
- MCP server dependency security — dependency pinning and advisory monitoring
- MCP server dependency pinning guide — exact version pinning for reproducible installs
- MCP server security checklist — comprehensive pre-submission checklist
Check your MCP server's web interface for missing SRI hashes on CDN-loaded resources.
Run a free audit → How grading works →