material-ui icon indicating copy to clipboard operation
material-ui copied to clipboard

[CSP] createCache nonce-value added to Cache not applied to inline-styles

Open Mateo-P opened this issue 2 years ago • 5 comments

Steps to reproduce 🕹

we are using remixjs+MUI and want to add CSP-Headers following these guides: CSP-guide remix-Styling

we added the nonce-value to the cache on CreateEmotionCache.tsx:

CreateEmotionCache.tsx
import createCache from '@emotion/cache';

import { cspScriptNonce } from '@/root';

export default function createEmotionCache() {
  return createCache({ key: 'css', nonce: cspScriptNonce });
}

here is how we have entry.server.tsx:

entry.server.tsx
import { CacheProvider } from '@emotion/react';
import createEmotionServer from '@emotion/server/create-instance';
import CssBaseline from '@mui/material/CssBaseline';
import { ThemeProvider } from '@mui/material/styles';
import type { EntryContext } from '@remix-run/node';
import { Response } from '@remix-run/node';
import { RemixServer } from '@remix-run/react';
import { renderToString } from 'react-dom/server';

import { theme } from '@/styles/theme';
import createEmotionCache from '@/styles/utils/createEmotionCache';

import { StateManagerProvider } from '@/lib/state-manager';

export default function handleRequest(
  request: Request,
  responseStatusCode: number,
  responseHeaders: Headers,
  remixContext: EntryContext,
): Response {
  const cache = createEmotionCache();
  const { extractCriticalToChunks } = createEmotionServer(cache);
  const html = renderToString(
    <StateManagerProvider>
      <CacheProvider value={cache}>
        <ThemeProvider theme={theme}>
          <CssBaseline />
          <RemixServer context={remixContext} url={request.url} />
        </ThemeProvider>
      </CacheProvider>
    </StateManagerProvider>,
  );

  const { styles } = extractCriticalToChunks(html);
  const loaderData = remixContext.staticHandlerContext.loaderData as {
    root: {
      cspScriptNonce: string | undefined;
    };
  };
  const nonce = loaderData.root.cspScriptNonce;
  let stylesHTML = '';
  styles.forEach(({ key, ids, css }) => {
    const emotionKey = `${key} ${ids.join(' ')}`;
    const newStyleTag = `<style nonce="${nonce}" data-emotion="${emotionKey}">${css}</style>`;
    stylesHTML = `${stylesHTML}${newStyleTag}`;
  });
  const markup = html.replace(
    /<meta(\s)*name="emotion-insertion-point"(\s)*content="emotion-insertion-point"(\s)*\/>/,
    `<meta name="emotion-insertion-point" content="emotion-insertion-point"/>${stylesHTML}`,
  );
  const { CSPolicy, CSPheaders } = createCSP(nonce!!);
  responseHeaders.set(CSPolicy, CSPheaders);
  responseHeaders.set('Content-Type', 'text/html');

  return new Response(`<!DOCTYPE html>${markup}`, {
    headers: responseHeaders,
    status: responseStatusCode,
  });
}

const createCSP = (nonce: string) => {
  const isDevelopment = process.env.NODE_ENV === 'development';
  let scriptSrc: string;
  if (typeof nonce === 'string' && nonce.length > 10) {
    scriptSrc = `'report-sample' 'nonce-${nonce}'`;
  } else if (isDevelopment) {
    // Allow the <LiveReload /> component to load without a nonce in the error pages
    scriptSrc = "'report-sample' 'unsafe-inline'";
  } else {
    scriptSrc = "'report-sample'";
  }

  const connectSrc = isDevelopment ? 'ws://localhost:*' : '';

  const CSPolicy = isDevelopment
    ? 'Content-Security-Policy-Report-Only'
    : 'Content-Security-Policy';

  const CSPheaders =
    "default-src 'self'; " +
    `script-src 'self' ${scriptSrc}; ` +
    `style-src 'self' ${scriptSrc}; ` +
    "img-src 'self' data: blob:; " +
    `connect-src 'self' ${connectSrc} data:; ` +
    "object-src 'none'; " +
    "worker-src 'self' blob:; " +
    "frame-ancestors 'none'; " +
    'block-all-mixed-content; ' +
    'report-to /reporting/csp';
  return { CSPolicy, CSPheaders };
};

Current behavior 😯

we added nonce-value to the <style/> tag in the entry.server.tsx but we are facing the errors:

Screenshot 2023-09-13 at 14 42 35

Expected behavior 🤔

nonce-value assigned to Cache added to <Style/> tags so that they are identified and nonce-values match

Context 🔦

we want to add CSP headers to harden security to our Remixjs app.

Your environment 🌎

`npx @mui/envinfo`
  Chrome, Safari, Firefox
 System:
    OS: macOS 13.5.2
  Binaries:
    Node: 18.16.1 - /usr/local/bin/node
    Yarn: 1.22.19 - /opt/homebrew/bin/yarn
    npm: 9.6.6 - /opt/homebrew/bin/npm
  Browsers:
    Chrome: 116.0.5845.187
    Edge: Not Found
    Safari: 16.6
  npmPackages:
    @emotion/react: 11.11.1 => 11.11.1 
    @emotion/styled: 11.11.0 => 11.11.0 
    @mui/base: 5.0.0-beta.14 => 5.0.0-beta.14 
    @mui/core-downloads-tracker:  5.14.8 
    @mui/material: 5.14.8 => 5.14.8 
    @mui/private-theming:  5.14.8 
    @mui/styled-engine:  5.14.8 
    @mui/system: 5.14.8 => 5.14.8 
    @mui/types:  7.2.4 
    @mui/utils:  5.14.8 
    @mui/x-data-grid:  6.13.0 
    @mui/x-data-grid-pro: 6.13.0 => 6.13.0 
    @mui/x-license-pro: 6.10.2 => 6.10.2 
    @types/react: 18.2.21 => 18.2.21 
    react: 18.2.0 => 18.2.0 
    react-dom: 18.2.0 => 18.2.0 
    typescript: 5.2.2 => 5.2.2 

Mateo-P avatar Sep 13 '23 21:09 Mateo-P

Hello @mnajdova 🤠
any updates regarding this?

Mateo-P avatar Sep 21 '23 14:09 Mateo-P

It's hard to know what may be miss-configured without a repo example to look into. Can you create a simpler reproduction and share link to the repository? Thanks!

mnajdova avatar Sep 27 '23 11:09 mnajdova

Since the issue is missing key information and has been inactive for 7 days, it has been automatically closed. If you wish to see the issue reopened, please provide the missing information.

github-actions[bot] avatar Oct 04 '23 11:10 github-actions[bot]

hi @mnajdova
Here is a repo to see the error: https://github.com/Mateo-P/nonce-problem.git

my thought is that the inconsistency is on the createEmotionCache function. why does it need to be executed on the client, entry.client.tsx? this is causing the nonce to have different values in the server and client. for instance, when hardcoding a nonce value the erros go away

Mateo-P avatar Oct 11 '23 15:10 Mateo-P

I have the same problem when applying nonce's described in the doc, but on a NextJS project. https://github.com/mui/material-ui/issues/40435 I think all cases where the style attribute is used should be replaced with a child style element. (This is done by the emotion dependency, but maybe it's not the case for the main source?)

mjulstein avatar Oct 15 '24 07:10 mjulstein