Search code examples
node.jscontent-security-policyhelmet.js

Ressources not loaded after setting CSP and CORP headers using Helmet


Trying to make the web-app safer and force myself to control better future additions (JS and CSS assets on different CDNs), I'm running Helmet plugin in my Fastify (same as Express) web-app.

If I deactivate all Helmet controls like the following: fastify.register(helmet, false) all works fine and all resources are loaded on client.

Then I tried to play (until exhaustion) with different configurations, nothing is working. The config and the browser error as the following:

{
    // contentSecurityPolicy: false,
    crossOriginResourcePolicy: { policy: 'same-site'}, 
    contentSecurityPolicy: {
      directives: {
        ...require("fastify-helmet").contentSecurityPolicy.getDefaultDirectives(),
        "default-src": ["'self'"],
        "style-src": ["'self'", "'unsafe-inline'", 'unpkg.com', 'cdn.jsdelivr.net',
          'fonts.googleapis.com', 'use.fontawesome.com'],
        "script-src": ["'self'", 'unpkg.com', "cdn.jsdelivr.net", "'unsafe-inline'"],
        "img-src": ["'self'", "'data'", "*.tile.osm.org"],
        "font-src": ["'self'", 'fonts.googleapis.com', 'fonts.gstatic.com', 'use.fontawesome.com']
      },
    },
  };

Even setting

{ contentSecurityPolicy: false, crossOriginResourcePolicy: { policy: 'same-site'} }

with other variations of policy: same-origin, cross-origin none seems to work.

errors

As you can see, I'm running on LOCALHOST too and I didn't test elsewhere.


Solution

  • tl;dr: disable the Cross-Origin-Embedder-Policy header, enabled by default in Helmet v5.

    {
      crossOriginEmbedderPolicy: false,
      // ...
    }
    

    Helmet maintainer here.

    Helmet sets the the Cross-Origin-Embedder-Policy HTTP response header to require-corp.

    Setting this header means that loading cross-origin resources (like an image from another resource) is trickier. For example, loading a cross-origin like this...

    <img alt="My picture" src="https://example.com/image.png">
    

    ...won't work unless example.com explicitly allows it, by setting some response headers of its own. Your browser will try to load example.com/image.png, and if it's not explicitly allowed, your browser will drop the response.

    To fix this, you can prevent Helmet from setting the Cross-Origin-Embedder-Policy header, like this:

    app.use(
      helmet({
        crossOriginEmbedderPolicy: false,
        // ...
      })
    );
    

    I made a small sample app you can use to play around with this.