How can I set a nonce in Material-UI for the Content-Security-Policy?
Asked Answered
P

4

20

I have a React App using Create-React-App (react-scripts) and Material-UI. I would like to apply a strong Content-Security-Policy for my app which does not allow unsafe inline styles.

I would like to set the CSP-Header server-side with a nonce present which can be done easily. However, Material-UI sets certain inline <style> tags dynamically at runtime without a nonce value as attribute.

I´ve seen the documentation on the Material-UI website under guides and csp. They seem to provide a solution. However, that solution is for server-side-rendering of the HTML, which I am not using. I am using Create-React-App and deliver the HTML, CSS and JavaScript statically.

Does anyone know how that can be achieved?

Prorate answered 6/3, 2018 at 7:33 Comment(4)
Create React App doesn't support Server Side Rendering, you may check Next.jsTb
I don't want Server Side rendering.Prorate
I've set the <meta property="csp-nonce" content="123456" /> in the <head> element as described on the Material-UI website under guides and csp, but the new <style> element that are added at runtime in the browser do not include the nonce, but look like this: <style type="text/css" data-jss data-meta-"MuiInputLabel" nonce>. The nonce attribute is there in the '<style>' tag but it has no value. Could anyone solve that?Prorate
How do you generate a new nonce on each request to index.html if you are only serving your site statically?Mccorkle
P
1

It works the way it is. The nonce value is set, even though it is not shown in the browser.

Prorate answered 9/3, 2018 at 13:18 Comment(2)
nonce value is of course shown in the csp-nonce header, do you think it is a security risk? anyone can see that nonce value and bind it to any malicious script, is it?Mccorkle
@Mccorkle were you able to get information on why setting the csp-nonce in the meta tag is safe?Stopped
C
5

The JSS CSP docs explain this much better than the MUI CSP docs, and provide examples for Express and Webpack. Basically, you need to set the same "nonce" in a special <meta> property called csp-nonce (which is read dynamically by JSS) and in your Content-Security-Policy header (either via another <meta> or a HTTP header) of public/index.html:

<meta http-equiv="Content-Security-Policy" content="default-src: 'self';
  style-src: 'self' 'nonce-xxxxxxxxxxxxxxxx=='">
<meta property="csp-nonce" content="xxxxxxxxxxxxxxxx==" />

If you can do this dynamically in whatever serves your index.html, then it is secure. (Obviously, an injected script could also read the nonce dynamically, but if that has happened, you've already lost). If you have to use a fixed value (e.g. because you're serving from a CDN), it's not secure but still arguably better than style-src: 'unsafe-inline', since an attacker at least needs to use your site-specific nonce.

As a hybrid approach when using a CDN, you can set a random nonce when the CDN fetches the page from the origin, as demonstrated here with an AWS Lambda@Edge. Then your nonce is only vulnerable in a specific region for the CDN cache TTL (which should be short for a mutable resource like index.html).

Cooky answered 18/11, 2020 at 2:31 Comment(0)
P
1

It works the way it is. The nonce value is set, even though it is not shown in the browser.

Prorate answered 9/3, 2018 at 13:18 Comment(2)
nonce value is of course shown in the csp-nonce header, do you think it is a security risk? anyone can see that nonce value and bind it to any malicious script, is it?Mccorkle
@Mccorkle were you able to get information on why setting the csp-nonce in the meta tag is safe?Stopped
G
1

I was able to get it working by following partly the documentation on MUI for SSR even though, we weren't using SSR.

Initially I tried adding webpack_nonce to our entry file, but that didn't apply nonce to mui styles.

This worked for me:

const cache = createCache({
  key: 'my-prefix-key',
  nonce: nonce,
  prepend: true,
});

function App(props) {
  return (
    <CacheProvider value={cache}>
      <Home />
    </CacheProvider>
  );
}
Greenery answered 15/3, 2023 at 8:0 Comment(2)
Can you please explain how you added webpack_nonce to your entry files?Druid
@OrNakash https://mcmap.net/q/626280/-how-can-i-set-a-nonce-in-material-ui-for-the-content-security-policy see my post belowSporocyst
S
0

If you are using webpack here is a instruction how to do it at least per build in SPA:

  1. add this into your html template(entry point, for ne it is index.html which is specified in WebPack):

<meta http-equiv="Content-Security-Policy" content="%%CSP_CONTENT%%">

  1. Install csp-html-webpack-plugin and go into your webpack:
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const CspHtmlWebpackPlugin = require('csp-html-webpack-plugin');

const generatedNonce = crypto.randomBytes(16).toString('base64');

module: {
    rules: [
        {
            test: /\.css$/,
                use: [
                {
                    loader: MiniCssExtractPlugin.loader,
                },
                'css-loader',
            ],
        },
    ],
},
plugins: [
    new webpack.DefinePlugin({
        'process.env.EMOTIONAL_NONCE': JSON.stringify(generatedNonce),
    }),
    new MiniCssExtractPlugin({
        filename: 'css/[name].[hash].css',
        chunkFilename: 'css/[id].[hash].css',
    }),
    new CspHtmlWebpackPlugin(
        {
            'base-uri': "'self'",
            'default-src': [
                "'self'"
            ],
            'object-src': "'none'",
            'script-src': "'self'",
            'style-src': [
                "'self'",
                `'nonce-${generatedNonce}'`,
            ],
        },
        {
            enabled: true,
            hashEnabled: {
                'style-src': false, 
            },
            nonceEnabled: {
                'style-src': false, 
            },
        }
    )
  1. Then as per this article from MUI(https://mui.com/material-ui/guides/content-security-policy/):
import createCache from '@emotion/cache';

const emotionalNonce = process.env.EMOTIONAL_NONCE;

const styleCache = createCache({
  key: 'YourCacheKey',
  nonce: emotionalNonce,
});

ReactDOM.render(
  <CacheProvider value={styleCache}>
    <Router>
      <App />
    </Router>
  </CacheProvider>,
  document.getElementById('root')
);

NOTE: for dynamic elements(aka modals MUI Modals), if you are rendering it via code don't forget to create another cache provider or reuse existing one and wrap the existing code, because it will be inserted NOT in root container of your React application by default.

Sporocyst answered 6/3 at 21:28 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.