graphile / starter

Opinionated SaaS quick-start with pre-built user account and organization system for full-stack application development in React, Node.js, GraphQL and PostgreSQL. Powered by PostGraphile, TypeScript, Apollo Client, Graphile Worker, Graphile Migrate, GraphQL Code Generator, Ant Design and Next.js
https://graphile-starter.herokuapp.com
Other
1.74k stars 219 forks source link

Empty white screen in Safari, due to Content Security Policy #237

Closed singingwolfboy closed 3 years ago

singingwolfboy commented 3 years ago

Summary

Safari apparently has a more restrictive default content security policy than Chrome or Firefox. This prevents the starter from displaying properly (or at all) in Safari.

Steps to reproduce

Visit https://graphile-starter.herokuapp.com using the latest version of Safari.

Expected results

The website should appear, and be usable.

Actual results

The website appears for a moment, but then is replaced with a blank white page. Opening the browser console reveals several security-related errors, that point to CSP violations.

safari-starter-broken

The most relevant errors are:

[Error] Refused to execute a script because its hash, its nonce, or 'unsafe-inline' does not appear in the script-src directive of the Content Security Policy. (graphile-starter.herokuapp.com, line 0) [Error] Refused to connect to wss://graphile-starter.herokuapp.com/graphql because it appears in neither the connect-src directive nor the default-src directive of the Content Security Policy.

Additional context

I am using Safari 14 on macOS Big Sur.

Possible Solution

Set a Content-Security-Policy header in the response. You can do this in the next.config.js file.

kh0r commented 3 years ago

The wss error can be explained by the definition of 'self' on the corresponding MDN docs for connect-src:

Refers to the origin from which the protected document is being served, including the same URL scheme and port number.

Safari correctly refuses the connection via wss, because it is a different origin than the https url.

A possible fix would be to add the corresponding URL schemes to the csp config here: (I also had to remove the upgrade-insecure-requests and block-all-mixed-content directives to make the site work on http://localhost in Safari)

export default function installHelmet(app: Express) {
  const {
    ["upgrade-insecure-requests"]: _uir,
    ["block-all-mixed-content"]: _bamc,
    ...defaultDirectives
  } = helmet.contentSecurityPolicy.getDefaultDirectives();
  app.use(
    helmet(
      isDevOrTest
        ? {
            // Dev needs 'unsafe-eval' due to
            // https://github.com/vercel/next.js/issues/14221
            contentSecurityPolicy: {
              directives: {
                ...defaultDirectives,
                "script-src": ["'self'", "'unsafe-eval'"],
                "connect-src": ["'self'", "ws:", "wss:"],
              },
            },
          }
        : {
            contentSecurityPolicy: {
              directives: {
                ...helmet.contentSecurityPolicy.getDefaultDirectives(),
                "connect-src": ["'self'", "ws:", "wss:"],
              },
            },
          }
    )
  );
}

Beware, this is only tested on http://localhost, not in a production setup with correctly setup TLS.

I'm also not sure on the implications of this allowing connections to any URLs using the ws and wss scheme.

benjie commented 3 years ago

I don't get the issue as described, but I do have websocket issues:

Screenshot 2021-01-29 at 15 37 23

If I specifically connect to the non-https version (note to self: fix that!) I get different errors:

Screenshot 2021-01-29 at 15 36 31

No blank page in either case. I'm going to find a Big Sur to try it on.

jemgillam commented 3 years ago

This is what I see from Safari on Big Sur

Screenshot 2021-01-29 at 15 44 01
benjie commented 3 years ago

MDN to the rescue on the websocket front

Note: connect-src 'self' does not resolve to websocket schemas in all browsers, more info: https://github.com/w3c/webappsec-csp/issues/7

-- https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/connect-src

Seems that Safari's had this issue for a while, so we can't use just 'self' and support Safari.

benjie commented 3 years ago

This is now solved (I believe).