Content Security Policy (CSP) Guide 

Content Security Policy (CSP) Guide 

Content Security Policy (CSP) is a computer security standard introduced to prevent code injection attacks resulting from malicious content in web pages. CSP provides a standard method for website owners to declare approved origins of content that browsers should be allowed to load on that website – covered types are JavaScript, CSS, HTML frames, web workers, fonts, images, embeddable objects such as Java applets, ActiveX, audio and video files, and other HTML5 features. (Wikipedia)

We have already implemented a Content Security Policy in the Scrivito Example App and thought we would walk you through it. A CSP is made up of directives for whitelisting approved sources of content and values. If the source is not listed, it is blocked. Sources are a set of named values, nonces, and even specific URLs. Lists of directives and sources can be found on the web. You can test a new CSP by setting it to “report only” mode (Content-Security-Policy-Report-Only) which will log blocked sources to the browser’s console. Once your policy is fine-tuned, you can remove the -Report-Only part, and the policy will block unapproved sources. The default-src is the fallback for most directives you didn’t include, so consider this your base.

The Example App’s CSP

The Scrivito Example App includes a recommended CSP in the public/_headersCsp.json file. The JSON format should make it easier to understand the structure of the header and to modify it. The CSP is determined for each individual page, allowing us to use the same policy for all pages.

  • In the case of Scrivito, preventing malicious JavaScript code from being loaded and executed is the most important. The script-src directive handles this by permitting only local sources.
    Never ever allow remote inline JavaScript with Scrivito!

The CSP implemented as a default in the Example App is as follows:

When building the Example App, the CSP header is constructed by converting the directives from JSON to the single-line format compliant with the HTTP protocol and inserting them into the “_headers” file, replacing the CSP-DIRECTIVES-PLACEHOLDER. Here, you can add -Report-Only to the header name for testing:

##
# Netlify headers. See https://www.netlify.com/docs/headers-and-basic-auth/ for details.
#
# CSP-DIRECTIVES-PLACEHOLDER will be replaced by ExtendCspHeadersWebpackPlugin,
# which uses _headersCsp.json as input.
##

/*
  Content-Security-Policy: CSP-DIRECTIVES-PLACEHOLDER;
  X-Frame-Options: sameorigin
  X-XSS-Protection: 1; mode=block
  X-Content-Type-Options: nosniff
  Referrer-Policy: strict-origin-when-cross-origin

The resulting “_headers” file follows Netlify’s conventions for specifying custom headers, which are fairly consistent with other providers. Like we did for the Example App, you can add further headers to the file according to your demands.

Let’s break the above policy down:

  • base-uri restricts the URLs that can appear in the <base> element of a page. We have specified 'none' as we do not want to restrict the URL/target for all relative URLs in a site.
  • default-src defines the defaults for most directives you leave unspecified. Here we include:
    • 'self' allows loading resources from the same origin (same scheme, host and port).
    • data: allows data: URIs to be used as a content source.
    • https: allows resources loaded over HTTPS.
    • wss: allows web socket connections.
  • style-src defines valid sources for stylesheets. The Example App includes:
    • 'self' allows loading resources from the same origin (same scheme, host and port).
    • data: allows data: URIs to be used as a style source.
    • https: allows resources loaded over HTTPS.
    • wss: allows web socket connections.
    • 'unsafe-inline' allows the use of inline resources such as event handlers, URLs, and styles.
  • script-src controls a set of script-related privileges, including JavaScript.
    • 'self' allows loading scripts from the same origin (same scheme, host and port).
    • https://api.scrivito.com grants the SDK access to additional scripts, e.g. for rate limiting.
    • https://assets.scrivito.com permits loading assets via this host.
    • https://app.intercom.io, https://js.intercomcdn.com and https://widget.intercom.io allows Intercom functionality.
  • object-src allows control over Flash and other plugins.
    • 'none' prevents loading objects from any source.
  • block-all-mixed-content: prevents loading any assets using HTTP when the page is loaded using HTTPS.

Note that for making it easier to find security issues in the CSP, it is also included in the local development environment. However, due to limitations with webpack's hot code reloading, the 'unsafe-inline' directive is added to script-src in development mode, potentially allowing editors to have remote scripts executed.

Permitting external sources

To whitelist an external source, just add its URL to the respective directives.

Wrapping up

A Content Security Policy can be tough to implement, but it will make your website much more secure. There are many resources on the web to learn more and see examples. It is important to test out changes to an existing site in report mode to prevent blocking needed functionality. Remember to evaluate how adding directives affects the default-src values as you will usually include some in the new directive.

The CSP included in the Scrivito Example App covers its initial implementation. After adding sources or services to your Scrivito-based website, you should re-evaluate the CSP to ensure that you are still protected against malicious attacks.