What is a Content Security Policy (CSP)?

A Content Security Policy (CSP) is a web security standard that adds a crucial layer of defense against content injection attacks, most notably Cross-Site Scripting (XSS). It is implemented via an HTTP response header (Content-Security-Policy) sent from the server to the browser.

The header's value specifies the trusted sources from which the browser is allowed to load resources such as scripts, styles, images, fonts, and frames. By creating an "allowlist" of trusted domains, you can prevent the browser from executing malicious scripts injected by an attacker.

Why is CSP Important?

XSS attacks occur when an attacker injects malicious scripts into a trusted website. When a victim visits the site, the malicious script executes in their browser, allowing the attacker to steal session cookies, deface the site, or redirect the user to a malicious page.

CSP provides a powerful mitigation by telling the browser: "Only execute scripts from self (my own domain) and https://apis.google.com. Ignore all others." This effectively blocks the malicious injected script from running.

How to Implement a CSP

Implementing a CSP is a multi-step process. It's crucial to start with a reporting-only policy to avoid breaking your site.

Step 1: Start with a Reporting-Only Policy

A reporting-only policy allows you to monitor potential CSP violations without actually blocking any content. This is a safe way to discover all the legitimate resources your site uses before you start enforcing the policy.

The header for this is Content-Security-Policy-Report-Only.

Example: A strict starting point

Content-Security-Policy-Report-Only: 
  default-src 'none'; 
  script-src 'self'; 
  connect-src 'self'; 
  img-src 'self'; 
  style-src 'self'; 
  report-uri https://your-csp-report-collector.com/report;

Let's break down these directives:

  • default-src 'none': This is the fallback. By default, nothing is allowed. This forces you to explicitly allowlist every type of resource.
  • script-src 'self': Allows scripts to be loaded from your own domain.
  • connect-src 'self': Allows AJAX requests, WebSockets, etc., to your own domain.
  • img-src 'self': Allows images from your own domain.
  • style-src 'self': Allows stylesheets from your own domain.
  • report-uri: This is crucial. The browser will POST a JSON report of any violation of this policy to the specified URL. You can use a service like report-uri.com or build your own collector.

Step 2: Monitor the Violation Reports

Deploy the Report-Only header to your website and wait for violation reports to come in. As you browse your site and users interact with it, you will receive reports for every resource that is loaded from a source not on your allowlist.

For example, you might get a report that a script from https://google-analytics.com was blocked. This tells you that you need to add it to your script-src directive.

Step 3: Refine Your Policy

Based on the reports, gradually build up your policy by adding the legitimate sources you discovered.

Example: An evolving policy

Content-Security-Policy-Report-Only: 
  default-src 'none'; 
  script-src 'self' https://google-analytics.com https://www.googletagmanager.com; 
  connect-src 'self' https://google-analytics.com; 
  img-src 'self' data: https://www.google-analytics.com; 
  style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; 
  font-src 'self' https://fonts.gstatic.com;
  report-uri https://your-csp-report-collector.com/report;

New additions:

  • We've added Google Analytics domains to script-src, connect-src, and img-src.
  • data: was added to img-src to allow for inline data URI images.
  • Google Fonts domains were added to style-src and font-src.
  • 'unsafe-inline' was added to style-src. This is often needed for legacy CSS but should be avoided if possible. Crucially, avoid 'unsafe-inline' and 'unsafe-eval' for script-src, as they undermine the primary goal of CSP.

Step 4: Move to Enforcement

Once you are confident that your policy covers all legitimate resources and you are no longer seeing violation reports for normal site operation, you can move to an enforcement policy.

To do this, simply change the header name from Content-Security-Policy-Report-Only to Content-Security-Policy.

Example: Final Enforcement Policy

Content-Security-Policy: 
  default-src 'none'; 
  script-src 'self' https://google-analytics.com https://www.googletagmanager.com; 
  connect-src 'self' https://google-analytics.com; 
  img-src 'self' data: https://www.google-analytics.com; 
  style-src 'self' https://fonts.googleapis.com; 
  font-src 'self' https://fonts.gstatic.com;
  report-uri https://your-csp-report-collector.com/report;

(Note: We aim to remove 'unsafe-inline' from style-src by refactoring CSS if possible).

Best Practices for a Strong CSP

  • Use a report-uri: Always include a report-uri (or the newer report-to) directive, even in enforcement mode. This will alert you to any new injection attacks being attempted against your site.
  • Avoid 'unsafe-inline' and 'unsafe-eval' for scripts: These directives largely defeat the purpose of CSP. If you must use inline scripts, use a nonce or a hash.
    • Nonce: A unique, randomly generated token for each request. You add the nonce to the CSP header (script-src 'nonce-rAnd0m') and to the script tag (<script nonce="rAnd0m">).
    • Hash: A SHA hash of the inline script's content. You add the hash to the CSP header (script-src 'sha256-...').
  • Use object-src 'none': Disallow plugins like Flash, which are a common attack vector.
  • Use frame-ancestors 'self': This directive helps prevent clickjacking attacks by controlling where your site can be framed.

Implementing a strong CSP is a powerful step towards securing your website. By starting with a Report-Only policy and iteratively refining it, you can deploy this critical security feature without breaking functionality.

Related Articles

© PEAKHOUR.IO PTY LTD 2025   ABN 76 619 930 826    All rights reserved.