Understanding CSP Bypass Risks with Public CDNs
Content Security Policy (CSP) allowlists domains you trust. But some domains serve arbitrary user-uploaded content, allowing attackers to bypass your policy.
The Problem
When you add a domain like cdn.jsdelivr.net to your CSP's script-src directive, you're trusting all scripts from that domain. Public CDNs serve packages uploaded by anyone:
- Attacker creates malicious npm package
totally-legit-library - Package is automatically available at
cdn.jsdelivr.net/npm/totally-legit-library - If attacker finds XSS on your site, they can inject:
<script src="https://cdn.jsdelivr.net/npm/totally-legit-library"></script> - Your CSP allows it because you trusted the entire domain
Affected CDNs
These public CDNs are known to serve arbitrary user content:
| Domain | Risk |
|---|---|
cdn.jsdelivr.net | Serves any npm package, GitHub repo, or WordPress plugin |
cdnjs.cloudflare.com | Serves popular JavaScript libraries (community-submitted) |
unpkg.com | Serves any npm package |
cdn.skypack.dev | Serves any npm package as ES modules |
esm.sh | Serves any npm package as ES modules |
esm.run | Alias for jsdelivr ES modules |
cdn.statically.io | Proxies content from GitHub, GitLab, Bitbucket |
Mitigations
1. Use Subresource Integrity (SRI)
Add integrity attributes to your script tags. The browser will verify the script's content matches the hash before executing:
<script
src="https://cdn.jsdelivr.net/npm/lodash@4.17.21/lodash.min.js"
integrity="sha384-..."
crossorigin="anonymous"
></script> ScriptAttest generates these hashes for you in the ScriptAttest tab, in the SRI Hashes section.
2. Pin Specific Versions and Paths
Instead of allowing the entire domain, use specific paths where possible:
// Less secure - allows any package
script-src cdn.jsdelivr.net;
// More secure - only allows specific package/version
script-src cdn.jsdelivr.net/npm/lodash@4.17.21/; Note: Path-based restrictions in CSP are limited. SRI is more reliable.
3. Monitor with ScriptAttest
Regular attestation scans detect when script content changes unexpectedly. Even if an attacker compromises a package, you'll be alerted to the change.
Defense in Depth
Even if an attacker loads malicious code through a CDN bypass, your CSP provides additional protection:
connect-src- Limits where scripts can send data (blocks exfiltration to attacker servers)form-action- Prevents form submissions to attacker domainsframe-ancestors- Prevents clickjacking
This layered approach means a supply chain attack through a CDN still can't easily exfiltrate your users' data.
Recommendations
- Add SRI hashes to all external script tags (see ScriptAttest tab)
- Run regular attestation scans to detect unexpected changes
- Keep
connect-srcrestrictive to limit what scripts can do - Consider self-hosting critical dependencies instead of using public CDNs