DEV Community

Hongster
Hongster

Posted on

Content Security Policy (CSP) : Understand in 3 Minutes

Problem Statement

Content Security Policy (CSP) is a browser security standard that tells your web application which sources of content (scripts, styles, images, etc.) are allowed to load and execute. You’ll run into CSP when a feature you deployed suddenly breaks in production—maybe a third‑party analytics script stops firing, or an inline <script> you added silently fails. The root cause? Your site’s CSP header is blocking it. If you’ve ever seen a console warning like “Refused to load the script because it violates the following Content Security Policy directive,” you’ve already met CSP.


Core Explanation

Think of CSP as a bouncer at a club. Your web page is the club, and every resource (JavaScript, CSS, images, fonts) is a guest. Without a bouncer, anyone can walk in—including malicious scripts injected via a cross‑site scripting (XSS) attack. CSP gives you a list of rules that the bouncer enforces: only guests from approved sources get in; everyone else is turned away.

How does it work? You send a Content-Security-Policy HTTP header (or a <meta> tag) from your server. Inside that header you define directives that specify allowed sources for different content types. The browser reads the policy and blocks any resource that doesn’t match.

Key components (directives) you’ll see most often:

  • default-src — the fallback for any directive not explicitly set. If a resource type isn’t covered, the browser uses this.
  • script-src — controls which scripts can execute. This is the most common directive you’ll tweak.
  • style-src — controls which stylesheets and inline styles are allowed.
  • img-src — controls image sources (often used to allow a CDN or data: URIs).
  • connect-src — controls which URLs your JavaScript can fetch via fetch(), XMLHttpRequest, WebSocket, etc.
  • report-uri / report-to — where the browser sends violation reports (useful for debugging).

A simple example directive set:

default-src 'self'; script-src 'self' https://cdn.example.com; img-src *

This says:

  • Everything defaults to only the same origin ('self').
  • Scripts can come from the same origin or from https://cdn.example.com.
  • Images can come from anywhere (*).

CSP values can be:

  • Host sources like https://example.com or *.example.com.
  • Schemes like https: or data:.
  • 'self' — matches the current origin.
  • 'none' — blocks everything.
  • 'unsafe-inline' — allows inline code (use sparingly, because it weakens protection).
  • 'unsafe-eval' — allows eval() and similar functions.
  • Nonces or hashes — for safely allowing specific inline scripts (modern best practice).

Practical Context

When to use CSP:

Apply CSP on any public-facing web application that handles user input, stores session data, or embeds third‑party content. It’s your first line of defense against XSS attacks. If you’re working on a banking app, an e‑commerce site, or a SaaS dashboard—you need CSP.

When NOT to use CSP:

  • Very small static sites with no user input and no third‑party scripts — the overhead might not be worth it.
  • During development if you’re actively debugging scripts that load from many untested sources. Better to start with a report-only policy first (using Content-Security-Policy-Report-Only instead of the enforcement header).
  • If you rely heavily on inline event handlers (like onclick="...") or eval() in your code — CSP will block those unless you use 'unsafe-inline' or 'unsafe-eval', but those weaken security. Consider refactoring.

Common real-world use cases:

  1. E‑commerce checkout — prevent an injected script from stealing credit card data.
  2. SaaS dashboards — allow analytics and A/B testing scripts from trusted CDNs while blocking everything else.
  3. Content management systems — restrict what external resources editors can embed (e.g., images only from whitelisted image hosts).

Why should you care?

XSS is still one of the most common web vulnerabilities. CSP makes it much harder for an attacker to inject malicious code, even if they find an injection point. It’s a defense-in-depth layer that browsers support universally. Plus, it’s required by many security compliance frameworks (OWASP Top 10, PCI DSS). Ignoring CSP means leaving your users’ browsers unprotected.


Quick Example

Before CSP: A page includes an inline <script> that sets a global variable, plus a third‑party analytics script:

<script>
  var userId = "12345";
</script>
<script src="https://analytics.example.com/tracker.js"></script>
Enter fullscreen mode Exit fullscreen mode

After enforcing CSP with this header:

Content-Security-Policy: default-src 'self'; script-src 'self' https://analytics.example.com;
Enter fullscreen mode Exit fullscreen mode
  • The inline <script> block will be blocked because 'unsafe-inline' is not allowed.
  • The analytics script from the whitelisted CDN will load normally.
  • Any attacker trying to inject <script>malicious()</script> will be blocked.

To fix the inline script, you’d replace it with an external JS file served from your own origin, or use a nonce (e.g., script-src 'nonce-abc123' and add nonce="abc123" to the <script> tag). This example shows exactly why CSP forces you to audit and refactor your code—and why it’s so effective.


Key Takeaway

Start with a report-only CSP and monitor violation reports before enforcing. This gives you a safe way to discover exactly what your site loads, without breaking anything. Once you have a clean report, switch to full enforcement. For deeper learning, read the MDN CSP guide.

Top comments (0)