React Content Security
Policy Guide: What It Is
and How to Enable It

stackhawk

StackHawk|November 8, 2021

Content Security Policy (CSP) applies to scripts, images, styles, and more. It helps prevent XSS. Learn about content security policy in React.

In this post, we're going to cover content security policy, or CSP, in React. First we'll have a brief overview of CSP—what is it good for anyway? Then we'll show you a few ways to enable CSP in React. React has a rich ecosystem that includes several frameworks and hosting platforms, and we can only cover a few of them here, but you'll get the idea. Last, there are a few caveats to be aware of, including some specific to React. Pay special attention to these.

Let's start with some basics.

What Is CSP?

A content security policy (CSP) protects web users from injected content. The policy is defined in page headers and is honored by all the major modern web browsers. The content security policy itself describes the content and sources of content that are allowed on a given web site or page. All other content is blocked by the browser. Let's look at an example of blocked content to make the example more clear. 

The simple React page below has a content security policy that implicitly disallows content from wikimedia.com.

React Content Security Policy Guide image

As you can see, the image is not rendered. The content security policy blocks it. The policy for this page only allows content from the host, the source, and one other domain (for loading React development scripts). The image itself is blocked by the browser. You can see this in the network tab of DevTools in Chrome.

React Content Security Policy Guide image

How to Enable Content Security Policy in React

You can enable a CSP in two different ways in a React app. The first is to add the headers directly to the response. The second is to add meta tags to the content. Note that meta tags aren't supported for some security headers, such as HSTS

It's good to know that you have options. Let's explore them, starting with a basic React app and ending with options for applying a CSP policy on the server. 

ReactJS

If you need to control a CSP using React code only, you're going to be using meta tags. Meta tags go in the head of the html document. Here's a basic example of how to accomplish this on the index page: 

<html>
  <head>
    ...
    <meta http-equiv="Content-Security-Policy" 
          content="...policy definition here..." />
  </head>
  <body>
     ...
     <div id="app-root"></div>
     ...
  </body>
</html>

The meta tag contains the content security policy. Browsers read and interpret meta tags in the head before processing the body. Still, you may also have content such as scripts and CSS that are loaded in the head. 

Order is important! If you want to apply the CSP to all the scripts that are loaded initially, put this meta tag before script or style tags. Alternatively, you could place the meta tag after scripts and style tags to avoid adding complexity to the CSP. Of course, doing so will diminish the effectiveness of the security mechanism.

There is an npm package named react-csp to help apply CSP to React apps, but it is of limited value compared with other options. The react-csp package simply adds syntactic sugar to how you add the CSP meta tag to the head. It does all the formatting too. There is certainly value in using it to create a meta tag as you get used to the syntax. To continue using it, however, you have to add another CLI command to your build script.

If you create a CSP in a pure React app, you'll need to keep track of everything. It would be much better to have more granular control. Next.js or some other SSR can give you that. 

Let's take a look at how to add CSP in Next.js.

Next.js

Next.js gives you more options for adding a CSP to your React app. Since Next.js renders the React app on the server prior to delivering it to the client, it can be more dynamic. Remember, delivering the CSP via meta tags only works when the meta tags are in the head of the document. If you put them in the body, you'll get an error that looks a bit like this: 

The Content Security Policy 'img-src upload.wikimedia.org' was 
delivered via a <meta> element outside the document's <head>, 
which is disallowed. The policy has been ignored.

So let's take a look at some options that will allow you to set your policy headers more dynamically.

Add CSP Headers in Your next.config.js

Until now, we've been looking at enabling CSP via meta tags. Now you're going to see how to add the policy in response headers. The way to tackle this in Next.js is to set the headers in the config file. The next.config.js file is a normal JavaScript file that controls several aspects of how Next.js behaves. One such option is to add response headers per page. Here's an example of what this configuration might look like:

async headers() {
  return [
    {
        source: '/',
        headers: [
            {
              "key": "Content-Security-Policy",
              "value": "default-src 'self' https: ; script-src 'self' ; object-src 'none'"
            }
        ]
    }
  ]
}

This configuration will only apply the policy to the root of the site. It doesn't allow for common glob patterns or typical regex syntax. 

Add Meta Tags Using next/head

You could add a CSP in meta tags using the Next.js built-in Head component. It's commonly used to update the page title and other page metadata. To add a CSP using this component, simply import it from next/head and use it as follows:

import Head from 'next/head'
...
return (
  <div>
    <Head>
      <meta http-equiv="Content-Security-Policy"
            content="default-src 'self' https: ; object-src 'none'" />
    </Head>
  </div>
)
...

This snippet will append a CSP to the head of the page. It will not overwrite or override existing policies. Speaking of adding policies, they only become more restrictive as you add more. In other words, they stack. A policy can never override a previous policy. This is important to keep in mind when using next/head to add CSP policies. 

Add a CSP Using next-strict-csp

Of course there's an npm package for working with CSP in Next.js as well. This option gives you two added benefits. One is a component that extends the Head component mentioned above. The NextStrictCSP component will automatically add your page scripts to the CSP. The second benefit is how it deals with inline scripts. It automatically adds those as well. 

See, inline scripts have to be dealt with in a special way in CSP. And next-strict-csp handles them quite nicely but without giving you much control over the options. Alas, it also appears that it's still in the early stages of development and has not yet dealt with issues such as inline styles and other types of content covered in CSP. Still, it could be a useful tool to have in mind. 

Web Server

You could also set your CSP headers via the web server. This won't always be an option and may not even be the best option, but it's possible. For example, an NGINX server can set response headers at the http, server, and location levels. This gives you some flexibility to set different policies for different contexts. It does not, however, make it easy to alter the policy along with the code. 

Let's say, for example, that you added a new script source from a third-party provider. In that case you would also need to update your CSP to allow the script to load. To do that, you'd have to update the NGINX configuration, deploy it, and restart the NGINX process. Going through those steps might not be your best option. Still, it's good to know the option exists.

Gotchas

React apps can often produce inline scripts. When you have a CSP enabled, this can cause problems because it will block your scripts from running unless you do something about it. There are a few options, depending on your use case. Here they are, from least to most secure: 

  1. Allow unsafe and inline scripts in your CSP for script-src types.

  2. Use nonces for your inline scripts (easier said than done).

  3. Use hash algorithms for your inline scripts (easier said than done as well).

  4. Don't use inline scripts or a CSS. INLINE_RUNTIME_CHUNK = false

From a practical standpoint, the first and last are the most reasonable. The latter takes a bit of work. 

Let's walk through a couple possibilities of how a CSP might be circumvented. 

  1. A script that loads before the meta tag might place unwanted content prior to a CSP meta tag.

  2. A third party between the user agent and the server might be able to add or remove tags or headers prior to delivering the response.

  3. Overly permissive policies might leave the page unprotected from nefarious content.

Additionally, Google Research published a document in 2016 outlining concerns with CSP. Their research indicated that over 99 percent of web pages that used a CSP were still vulnerable to cross-site scripting (XSS) by other means of circumvention. 

Add Automated Security Testing to Your Pipeline

Final Thoughts

A CSP is one of several security controls for the web. As you've seen, it's somewhat complex to manage, and it can be thwarted. Do implement a CSP, but take the time to make sure you have a good understanding of the policy as a whole.  

This post described some methods and tools for implementing a content security policy, but it's not possible to cover all the nuances you may encounter. You'll inevitably encounter some edge cases, but hopefully you've learned enough here to deal with them as they arise. 

This post was written by Phil Vuollet. Phil leads software engineers on the path to high levels of productivity. He writes about topics relevant to technology and business, occasionally gives talks on the same topics, and is a family man who enjoys playing soccer and board games with his children.


StackHawk  |  November 8, 2021