graphql-playground icon indicating copy to clipboard operation
graphql-playground copied to clipboard

Not loading due to Content Security Policy Directive on CDN requests

Open tconroy opened this issue 3 years ago • 26 comments

This issue pertains to the following package(s):

  • [ ] GraphQL Playground - Electron App
  • [ ] GraphQL Playground HTML
  • [x] GraphQL Playground
  • [x] GraphQL Playground Express Middleware
  • [ ] GraphQL Playground Hapi Middleware
  • [ ] GraphQL Playground Koa Middleware
  • [ ] GraphQL Playground Lambda Middleware

What OS and OS version are you experiencing the issue(s) on?

MacOS 10.15.6 (Catalina), all browsers.

What version of graphql-playground(-electron/-middleware) are you experiencing the issue(s) on?

latest

What is the expected behavior?

I would expect to load the GraphQL playground.

What is the actual behavior?

Stuck at "Loading GraphQL Playground" screen with the following errors in console:

Refused to load the image 'http://cdn.jsdelivr.net/npm/@apollographql/[email protected]/build/favicon.png' because it violates the following Content Security Policy directive: "img-src 'self' data:".

graphql:1 Refused to load the script 'https://cdn.jsdelivr.net/npm/@apollographql/[email protected]/build/static/js/middleware.js' because it violates the following Content Security Policy directive: "script-src 'self'". Note that 'script-src-elem' was not explicitly set, so 'script-src' is used as a fallback.

graphql:531 Refused to execute inline script because it violates the following Content Security Policy directive: "script-src 'self'". Either the 'unsafe-inline' keyword, a hash ('sha256-PT+YOJyhu3IamY7Pf1cnvQbDxlHIK2FjqtA7GQoyN5U='), or a nonce ('nonce-...') is required to enable inline execution.

graphql:1 Refused to load the image 'https://cdn.jsdelivr.net/npm/@apollographql/[email protected]/build/favicon.png' because it violates the following Content Security Policy directive: "img-src 'self' data:".

What steps may we take to reproduce the behavior?

Attempt to visit graphql-playground.

Please provide a gif or image of the issue for a quicker response/fix. image

tconroy avatar Oct 04 '20 17:10 tconroy

@tconroy I could get rid of a view errors with passing these options:

const playground: PlaygroundConfig = {
  cdnUrl: 'https://cdn.jsdelivr.net/npm',
  faviconUrl: '',
};

The two errors related to index.css and middleware.js still remain.

    //...
    "apollo-server-core": "^2.18.1",
    "apollo-server-express": "^2.18.1",
    "express": "^4.17.1",
    "graphql": "^15.3.0",
    "graphql-tools": "^6.2.3",

bke-daniel avatar Oct 05 '20 11:10 bke-daniel

I had the same problem and solved it. This is a problem with setting the "Content-Security-Policy" header value that the web server responds. image need to delete or modify the setting.

In my case, the helmet module was responding, and I solved it by modifying it as follows.

app.use(helmet({ contentSecurityPolicy: (process.env.NODE_ENV === 'production') ? undefined : false }));

CatsMiaow avatar Oct 05 '20 13:10 CatsMiaow

Ah, brilliant! That was the same case as me @CatsMiaow -- was using a boilerplate and wasn't aware of the Helmet config. The value you set works perfectly. Thank you for the help!

tconroy avatar Oct 05 '20 20:10 tconroy

Where does the helmet config live? I've had a similar issue open for over a year (#1069) and I have no idea where to make this modification. I have no idea what helmet even is; the playground in my case is bundled with ... I'm not even sure, to be honest. Presumably one of these:

    "@apollo/gateway": "^0.6.13",
    "apollo-datasource-rest": "^0.5.0",
    "apollo-server": "^2.9.5",
    "apollo-server-testing": "^2.8.0",

pavellishin avatar Oct 21 '20 21:10 pavellishin

I had the same problem and solved it. This is a problem with setting the "Content-Security-Policy" header value that the web server responds. image need to delete or modify the setting.

In my case, the helmet module was responding, and I solved it by modifying it as follows.

app.use(helmet({ contentSecurityPolicy: (process.env.NODE_ENV === 'production') ? undefined : false }));

Is there any issue in setting contentSecurityPolicy to undefined in production?

alex-r89 avatar Nov 08 '20 23:11 alex-r89

https://github.com/helmetjs/helmet/blob/d491d281eb1cc55380046532d24fbc314af836e0/index.ts#L69-L75 undefined is the default. When undefined, it is enabled as the default option.

CatsMiaow avatar Nov 09 '20 01:11 CatsMiaow

Disabling contentSecurityPolicy for development is not the best idea, since developers will not notice something does not work until it's in the production.

I think the graphql playground should have all the scripts as npm development dependencies and not hosted on cdn.jsdelivr.net.

Another solution is whitelisting cdn.jsdelivr.net in CSP, but it is not a good idea, since anyone can upload anything on jsdelivr.net, which makes the CSP useless.

zsolt-dev avatar Jul 07 '21 09:07 zsolt-dev

This should be reopened. The root of issue lays in insecure playground implementation - this should be addressed based on issues reported by CSP checks. Like get rid of all inline scripts/styles/fonts/images and use whitelisted domain only or locally served files. Then it should be simple to configure helmet without using insecure directives/options.

tlenex avatar Nov 08 '21 10:11 tlenex

Like get rid of all inline scripts/styles/fonts/images and use whitelisted domain only or locally served files.

I'm only tangentially familiar with this particular issue, but why not use inline styles and other assets for the gql playground? This would also make it functional when offline, and as far as I'm aware, the playground is typically used for development, not for users in production.

pavellishin avatar Nov 08 '21 17:11 pavellishin

Well you don't want to disable CSP headers for development envs, because you will miss other issues with it, up until production, on your API. Therefore your playground is not working anyway or you introduce security issue with header policies to the whole app or you waste some resources and create a separate app with different header policy just for playground. The issue is even more serious if you plan to use playground as public and open API tool.

The other approach for this might be generating proper exclusions for CSP headers, I think.

tlenex avatar Nov 08 '21 18:11 tlenex

And what about the opposite? @zsolt-dev

const appConfig = app.get(AppConfigService);
app.use(helmet({ contentSecurityPolicy: (appConfig.environment === 'localhost') ? false : undefined }));

That way we have enabled CSP in other environments but localhost...

PD: yes this only works if you don't want to publish the playground...

Ismaestro avatar Nov 29 '21 15:11 Ismaestro

For those of you running into this after upgrading helmet to v5 or higher (as I did), you will also get a console error and blocked response because of COEP/CORP headers (in Chrome, but not in Firefox). This happens because the initial request to cdn.jsdelivr.com yields a 307 Temporary Redirect from http to https (because of the HSTS configuration on the jsdelivr side). This redirect itself does not contain the proper CORP headers -- I think.

I have addressed this at jsdelivr's repo: https://github.com/jsdelivr/jsdelivr/issues/18201#issuecomment-1012912236

To circumvent this, also disable the crossOriginEmbedderPolicy header:

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

kweij avatar Jan 14 '22 08:01 kweij

@kweij you're the hero of the day, thanks for taking the time to add this precise comment, thanks!!!

ramayac avatar Jan 14 '22 21:01 ramayac

@bke-daniel Your suggestion solved my issue. Thanks!

AhmedBHameed avatar Jan 15 '22 15:01 AhmedBHameed

I have filed a bug at Chrome for this (the COEP / CORP headers) issue, as it seems that this is not the way the browser should handle this (i.e.: HSTS redirects should be internal to the browser and not expect CORP headers):

https://bugs.chromium.org/p/chromium/issues/detail?id=1287500

EDIT: A fix has been made Jan 19, 2022 👍

kweij avatar Jan 17 '22 07:01 kweij

I'm facing this currently, localhost:4000/graphql was working fine an hour back and now it's gone down. First the request took a long time to fetch and then when I reinstalled all of my modules, the apollo server doesn't even run

AnimeshRy avatar Mar 04 '22 17:03 AnimeshRy

If you want to still have CSP headers but limit it somewhat (instead of disabling entirely)

  const devContentSecurityPolicy = {
    directives: {
      scriptSrc: ["'self'", "'unsafe-inline'", 'https://cdn.jsdelivr.net'],
      imgSrc: ["'self'", 'data:', 'https://cdn.jsdelivr.net'],
    },
  };

  app.use(
    helmet({
      // when undefined it will load the default option: https://github.com/graphql/graphql-playground/issues/1283#issuecomment-723705276
      contentSecurityPolicy: IS_DEV ? devContentSecurityPolicy : undefined,
    }),
  );

jensbodal avatar Apr 27 '22 23:04 jensbodal

I had the same problem and solved it. This is a problem with setting the "Content-Security-Policy" header value that the web server responds. image need to delete or modify the setting.

In my case, the helmet module was responding, and I solved it by modifying it as follows.

app.use(helmet({ contentSecurityPolicy: (process.env.NODE_ENV === 'production') ? undefined : false }));

Thanks you so much, I was trying to setup the graphql playground but it stayed at the loading..., until I google for a some minutes I came across of this post. thanks

kolpaja avatar Aug 10 '22 19:08 kolpaja

Correct me if I'm wrong but this issue is still ongoing as there is no current fix for production Open APIs serving this playground.

JiggyJinjo avatar Sep 27 '22 15:09 JiggyJinjo

Adding headers to ApolloServerPluginLandingPageLocalDefault options worked for me. Versions:

"@apollo/server": "^4.0.3",
"@apollo/server-plugin-landing-page-graphql-playground": "^4.0.0",
ApolloServerPluginLandingPageLocalDefault({
   headers: {
      "Content-Security-Policy": "default-src 'self' https://apollo-server-landing-page.cdn.apollographql.com;",
   },
}),

krisgardiner avatar Oct 26 '22 14:10 krisgardiner

Adding headers to ApolloServerPluginLandingPageLocalDefault options worked for me. Versions:

"@apollo/server": "^4.0.3",
"@apollo/server-plugin-landing-page-graphql-playground": "^4.0.0",
ApolloServerPluginLandingPageLocalDefault({
   headers: {
      "Content-Security-Policy": "default-src 'self' https://apollo-server-landing-page.cdn.apollographql.com;",
   },
}),

Unfortunately, this does not work for me. Maybe I need to define different settings?

Refused to load the image 'https://apollo-server-landing-page.cdn.apollographql.com/_latest/assets/favicon.png' because it violates the following Content Security Policy directive: "img-src 'self' data:".

graphql:22 Refused to load the image 'https://apollo-server-landing-page.cdn.apollographql.com/_latest/assets/favicon.png' because it violates the following Content Security Policy directive: "img-src 'self' data:".

graphql:1 Refused to load the script 'https://embeddable-sandbox.cdn.apollographql.com/_latest/embeddable-sandbox.umd.production.min.js?runtime=%40apollo%2Fserver%404.7.4' because it violates the following Content Security Policy directive: "script-src 'self'". Note that 'script-src-elem' was not explicitly set, so 'script-src' is used as a fallback.

graphql:71 Refused to execute inline script because it violates the following Content Security Policy directive: "script-src 'self'". Either the 'unsafe-inline' keyword, a hash ('sha256-OK7pAH5PWuMz0B3Z+8KJD+AVvhgLxb2X4W00TmJw2M8='), or a nonce ('nonce-...') is required to enable inline execution.

Refused to load the image 'https://apollo-server-landing-page.cdn.apollographql.com/_latest/assets/favicon.png' because it violates the following Content Security Policy directive: "img-src 'self' data:".

graphql:1 Refused to load manifest from 'https://apollo-server-landing-page.cdn.apollographql.com/_latest/manifest.json' because it violates the following Content Security Policy directive: "default-src 'self' Note that 'manifest-src' was not explicitly set, so 'default-src' is used as a fallback.

Any hint is appreciated!

tobiasschweizer avatar Jun 22 '23 07:06 tobiasschweizer

@krisgardiner Are you still using the same setup? I saw "@apollo/server-plugin-landing-page-graphql-playground" is now deprecated.

My setup is now as in https://docs.nestjs.com/graphql/quick-start#apollo-sandbox, but it does not work.

The HTML source looks as follows:

<meta http-equiv="Content-Security-Policy" content="script-src 'self' 'nonce-b5693f538f65c3b4938853dfcf85ff99c16ab6ef4dc5341e87b0c69ccbafa012' https://apollo-server-landing-page.cdn.apollographql.com https://embeddable-sandbox.cdn.apollographql.com https://embeddable-explorer.cdn.apollographql.com; style-src 'nonce-b5693f538f65c3b4938853dfcf85ff99c16ab6ef4dc5341e87b0c69ccbafa012' https://apollo-server-landing-page.cdn.apollographql.com https://embeddable-sandbox.cdn.apollographql.com https://embeddable-explorer.cdn.apollographql.com https://fonts.googleapis.com; img-src https://apollo-server-landing-page.cdn.apollographql.com; manifest-src https://apollo-server-landing-page.cdn.apollographql.com; frame-src https://explorer.embed.apollographql.com https://sandbox.embed.apollographql.com https://embed.apollo.local:3000" />
 
...

<script nonce="b5693f538f65c3b4938853dfcf85ff99c16ab6ef4dc5341e87b0c69ccbafa012">

  var initialEndpoint = window.location.href;
  var embeddedSandboxConfig = {"target":"#embeddableSandbox","initialState":{"headers":{"Content-Security-Policy":"default-src \u0027self' https://apollo-server-landing-page.cdn.apollographql.com;"}},"hideCookieToggle":false,"endpointIsEditable":false,"runtime":"@apollo/[email protected]","runTelemetry":true,"allowDynamicStyles":false};
  new window.EmbeddedSandbox(
   {
   ...embeddedSandboxConfig,
   initialEndpoint,
   }
   );
   </script>


tobiasschweizer avatar Jul 06 '23 13:07 tobiasschweizer

It was a config issue with content security policy, see https://docs.nestjs.com/security/helmet

tobiasschweizer avatar Jul 07 '23 14:07 tobiasschweizer

for anyone who might be interested, here is an example of how to load the new graphiql as an apollo server plugin:

https://codesandbox.io/p/sandbox/apollo-server-w-graphiql-8xbnju

we are pushing forward on adding the monaco-graphql mode that I created as well for 4.0.0

acao avatar Jul 08 '23 03:07 acao

I just spent a good 2 days of my life that I will never get back trying to figure this out -- the config in the nestjs docs worked beautifully. Thank you!

johnson-elugbadebo avatar Jan 25 '24 02:01 johnson-elugbadebo

@johnson-elugbadebo oh man thanks this works so fine!

saegeullee avatar Feb 15 '24 11:02 saegeullee