Using Content Security Policy headers with React & emotion

using-content-security-policy-headers-with-react-&-emotion

Content Security Policy (CSP) headers add another layer of security by disallowing unsafe actions, such as establishing connections with arbitrary domains, usage of eval, inline scripts, and others. This article will focus on the style-src directive and its usage with emotion.

Using CSP headers

Content-Security-Policy header should be set in the response to the browser when the application page is requested (e.g. index.html). It looks like this:

Content-Security-Policy: style-src self;

style-src is a directive specifying which styles are allowed on the page. Possible values include:

  • self – styles served from the same domain
  • URL, e.g. https://example.test
  • unsafe-eval – dynamically creating stylesheets from strings, e.g. by CSSStyleSheet.insertRule()
  • unsafe-inline – inline

    tags

  • nonce-, e.g. nonce-abc – allows creating inline style tags only with the specified nonce attribute value. The value should be coming from a cryptographically secure random token generator and re-created on every request.

Modifying element styles via DOM APIs that do not involve CSS parsing is allowed unless JS execution is blocked by the script-src directive. The following would work:

element.style.display = "none";

The following will not work unless unsafe-eval is enabled:

element.setAttribute("style", "display: none;");

CSP headers and emotion

emotion inserts inline style tags and cannot be currently configured to extract styles to a static file at build-time. It means that if we need to enable CSP headers with emotion the following options are available:

  1. unsafe-inline. This option doesn’t require any frontend code changes but it will not bring any security benefits.
  2. nonce-. This option can be used to only allow inline style tags created by emotion in the app. It is the most secure approach with emotion. To make it work, emotion needs to be aware of the nonce value set in the page response headers. The exact configuration depends on how emotion is used in your app.

If you are using @emotion/react or @emotion/styled you would need to provide a custom cache with nonce set:

import { CacheProvider } from '@emotion/react'
import createCache from '@emotion/cache'

export function App() {
  const cache = createCache({
    key: 'my-app',
    nonce: getNonceValue(),
  });

  return (
    
      ...
    
  );
}

If you are using @emotion/css you would need to create a custom emotion instance:

import createEmotion from '@emotion/css/create-instance'

export const {
  flush,
  hydrate,
  cx,
  merge,
  getRegisteredStyles,
  injectGlobal,
  keyframes,
  css,
  sheet,
  cache
} = createEmotion({
  key: 'my-app',
  nonce: getNonceValue(),
});

Please note that with this approach you would need to change all places where you previously used @emotion/css directly to import the module where createEmotion is called:

// import { css } from "@emotion/css"; 
import { css } from "./emotion.ts";

Passing nonce value to the frontend

CSP headers cannot be inspected by the frontend, so the value should be also set elsewhere. One of the common approaches is to set the value in an inline script tag by the backend:


The frontend could use it later like this:

function getNonceValue() {
  const nonceElement = document.getElementById("nonce");
  return JSON.parse(nonceElement.textContent);
}

Please note that there is a custom type attribute on the script tag. This is required to make the browser ignore the script body and not to execute it, which won’t work with certain script-src directive values.

Total
0
Shares
Leave a Reply

Your email address will not be published. Required fields are marked *

Previous Post
how-to-close-a-project-in-prince2

How To Close A Project In PRINCE2

Next Post
how-to-manage-project-tolerances-(with-prince2-7-guide)

How to manage project tolerances (with PRINCE2® 7 guide)

Related Posts