Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CMS does not work with Content Security Policy (CSP). Requires unsafe-eval / unsafe-inline for script-src / style-src #2138

Open
ghost opened this issue Mar 4, 2019 · 21 comments · May be fixed by #6106
Labels

Comments

@ghost
Copy link

ghost commented Mar 4, 2019

Describe the bug
I have been using Netlify CMS for sites without a content security policy. I am now trying to increase the security of my sites by adding this.
The CSP requires defining what sources are allowed for scripts, styles, frames, objects, etc.
The CSP also blocks things like eval in any JS execution and also generally inline-scripts or styles.
While it is possible to globally allow these things by setting unsafe-inline or unsafe-eval as permitted, this is not recommended, as it defeats the purpose of the CSP.

Here is the CSP I am applying to one of my sites via special file that Netlify recognises on deploy:

/*
  X-Frame-Options: sameorigin
  X-XSS-Protection: 1; mode=block
  X-Content-Type-Options: nosniff
  Referrer-Policy: strict-origin-when-cross-origin
  Content-Security-Policy: default-src 'self'; font-src 'self' fonts.googleapis.com https://use.fontawesome.com https://fonts.gstatic.com; img-src 'self'; object-src 'self' https://www.netlify.com https://identity.netlify.com; script-src 'self' https://www.google.com https://www.googletagmanager.com https://www.gstatic.com https://identity.netlify.com https://cdn.jsdelivr.net; style-src 'self' https://fonts.googleapis.com https://use.fontawesome.com; frame-src https://www.google.com 

As you can see, I have already attempted to somehow make anything coming from Netlify permissible, but Firefox states (among other things):

Content Security Policy: The page’s settings blocked the loading of a resource at eval (“script-src”)
Content Security Policy: The page’s settings blocked the loading of a resource at inline (“script-src”)

To Reproduce
Deploy any site to Netlify with a Content Security Policy which does not permit unsafe-eval or unsafe-inline

Expected behavior
Once Netlify has been added as permitted source for scripts, styles, etc. for this to allow loading the CMS. This may require a way to load the Netlify CMS without an inline-script tag, but I am not seeing this issue with other scripts, like google tag manager.
Eval may be a bigger issue, as that would require a change to the CMS' code and eval maybe unavoidable.

Applicable Versions:
Tested on:

  • Firefox 65.01, Manjaro Linux
  • Chrome 72, Manjaro Linux
  • Chrome 72, Windows 10

CMS configuration
not relevant for this, presumably

@ghost
Copy link
Author

ghost commented Mar 4, 2019

I should add that I was able to resolve some of the non-eval issues by using sha256 hashes in script-src (not the ones generated here, but only the ones Google Chrome shows when an element is blocked). The eval issue remains, however, and further inline-styles are required once you get past the initially blocked evals / inlines.
It would appear that some of the initial issues were caused by the Netlify Identity widget, but once those are out of the way, further issues crop up when trying to load /admin/

edit:

Because I can't get very clear error messages from Firefox or Chrome, this issue is very hard to diagnose. It seems as though the issues start with the Netlify identity widget and then continue when trying to load the CMS. The identity widget throws an script-src eval-violation in Firefox, but not in Chrome. In Chrome, the identity widget throws a style-src unsafe-inline violation, which does not show in Firefox.

In Firefox, this is where the buck stops.
In Chrome, I can advance to the /admin/ URL, where the CMS itself will provide the following error EvalError: Refused to evaluate a string as JavaScript because 'unsafe-eval' is not an allowed source of script in the following Content Security Policy directive: "script-src 'self' 'sha256-Y1ZIa2N59dlCRGtBP67/dCOpq2eDNPEO+vGJU0pjvEw=' 'sha256-jILZY2UBo/ux6K6qGHntQonoJYNQZfxdLrszRmDpc/o=' 'sha256-v40OvYf1mF3VQM75141gVEb6xZk97Wd9tDERTFg3N38=' 'sha256-tphCtOfPA2HxpbQIjyGzEOxfajrm+crnavJGIhdlT7k=' https://www.google.com https://www.googletagmanager.com https://www.gstatic.com https://identity.netlify.com https://cdn.jsdelivr.net https://www.google-analytics.com".

Chrome also throws multiple inline-style errors and the eval error mentioned before. I believe that without unsafe-inline and unsafe-eval it won't be possible to run either the identity widget or the CMS.

For the style-src issues I think it's down to react components' usage of inline-styles. For eval, you have to look further as it's not just eval itself, but it's also related functions.

I found a discussion regarding react from 2016 and one from 2018 regarding this.

In a recent e-mail conversation, Netlify have confirmed that they have a feature request to fix this, but are not actively looking into it, so for now I shall give up. I'll leave this information here for someone to revisit one day.

@ghost ghost changed the title CMS does not work with Content Security Policy (CSP). Requires unsafe-inline / unsafe-eval (which more or less disables CSP) CMS does not work with Content Security Policy (CSP). Requires unsafe-eval Mar 4, 2019
@ghost ghost changed the title CMS does not work with Content Security Policy (CSP). Requires unsafe-eval CMS does not work with Content Security Policy (CSP). Requires unsafe-eval / unsafe-inline Mar 4, 2019
@ghost ghost changed the title CMS does not work with Content Security Policy (CSP). Requires unsafe-eval / unsafe-inline CMS does not work with Content Security Policy (CSP). Requires unsafe-eval Mar 4, 2019
@ghost ghost changed the title CMS does not work with Content Security Policy (CSP). Requires unsafe-eval CMS does not work with Content Security Policy (CSP). Requires unsafe-eval / unsafe-inline for script-src / style-src Mar 5, 2019
@stale
Copy link

stale bot commented Oct 29, 2019

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

@callmemagnus
Copy link

I tried to create my own local version of the script. It worked until I wanted to add cloudinary with triggers the same issues.

Is there a way around this ?

@naton
Copy link

naton commented Jun 29, 2021

I second the question, is there any way around having to add unsafe-* stuff in the CSP?

@alexandrecz
Copy link

Hi guys...
any chance to ADMIN works without unsafe?

@erezrokah
Copy link
Contributor

I think this might be related to one of the dependencies we're using #4367 (comment)

@alexandrecz
Copy link

alexandrecz commented Oct 27, 2021

Thanks for answer!
Do you think we can use CMS without that dependency?

@erezrokah
Copy link
Contributor

No sure actually, it requires some research. We'll be happy to get a contribution for it if someone does the research on it.

@taras
Copy link

taras commented Nov 5, 2021

It looks like the issue might be caused by AJV creating a function at runtime to compile JSON schema into executable code. It's described here ajv-validator/ajv#406

One possible solution is to use standalone mode to compile schemas to code at build time.

@taras
Copy link

taras commented Nov 5, 2021

After a bit more digging, we might have a way to eliminate this problem by compiling schemas during build time.

On https://github.com/netlify/netlify-cms/blob/dbf2920254fb3682e12463a6df8ded4b94b55be0/packages/netlify-cms-core/src/constants/configSchema.js#L364, we'd need to use compiled schema.

To compile schema, we can use AJV Standalone to create executable code that we store as a JavaScript module. There is a Webpack loader that can be used to take care of some of this, but this loader is using ajv-pack which is deprecated.

It looks like the game plan here would be,

  1. Update https://github.com/downace/ajv-json-loader to use AJV 7 and Standalone mode
  2. Configure webpack config to use the updated loader
  3. Refactor getConfigSchema to read value from a module import
  4. Profit $$$

dagda1 added a commit to thefrontside/netlify-cms that referenced this issue Nov 22, 2021
dagda1 added a commit to thefrontside/netlify-cms that referenced this issue Dec 6, 2021
dagda1 added a commit to thefrontside/netlify-cms that referenced this issue Dec 7, 2021
dagda1 added a commit to thefrontside/netlify-cms that referenced this issue Dec 7, 2021
dagda1 added a commit to thefrontside/netlify-cms that referenced this issue Dec 7, 2021
dagda1 added a commit to thefrontside/netlify-cms that referenced this issue Dec 7, 2021
dagda1 added a commit to thefrontside/netlify-cms that referenced this issue Dec 7, 2021
dagda1 added a commit to thefrontside/netlify-cms that referenced this issue Dec 7, 2021
dagda1 added a commit to thefrontside/netlify-cms that referenced this issue Dec 7, 2021
dagda1 added a commit to thefrontside/netlify-cms that referenced this issue Dec 7, 2021
dagda1 added a commit to thefrontside/netlify-cms that referenced this issue Dec 7, 2021
dagda1 added a commit to thefrontside/netlify-cms that referenced this issue Dec 7, 2021
@cowboyd
Copy link

cowboyd commented Dec 21, 2021

@erezrokah After doing some more digging, coding, and research, we're really close on this, but there is one final stumble that we're hitting.

We were able to completely extract the schema, and build the validateConfig function at build time. This works for our site, but there are cases where it will not work for everyone. Specifically, the schema itself can change dynamically based on widget schemas that are added via registerWidget() https://github.com/netlify/netlify-cms/blob/2877f7983e9b5f4ff0cb2cdaaf6549675af68913/packages/netlify-cms-core/src/constants/configSchema.js#L75

Unfortunately, registering widgets happens in the browser, and so it cannot be known at build-time what the complete schema will be including widget extensions.

It seems to me that the only way this can work correctly is to deprecate registration of pieces of schema along with widgets and instead make the schema for user-supplied widgets sufficiently broad to type-check against any widget config. That's a breaking change and would mean slightly less type-safety for config.yaml, but it also means that it can be deployed with a secure content policy because the entire schema would be known at build time.

We're investing so much effort in this because we really like Netlify CMS, but unsafe-eval is unfortunately a deal breaker for our security team. We'd love to find a way forward here, but not really sure what to do at this point.

@erezrokah
Copy link
Contributor

erezrokah commented Dec 21, 2021

Thanks for the additional information @cowboyd and effort you're putting into this. Is there a way to put this change behind an option?
Then users should be able to opt-in to the breaking change.

We could always generate the static schema during build, the generate the dynamic schema by default, or let users opt out of the last part if they'd like.

I also added this to #5652

@cowboyd
Copy link

cowboyd commented Dec 21, 2021

A compromise option that might be viable is to only do dynamic validation if we detect that we need to because there are widgets registered with custom schemas. Widgets registered that do not have any additional schema would not require any dynamic validation.

// defined at build time
import { staticValidateConfig } from './config.schema.json';

export function validateConfig(config) {
  if (Object.values(getWidgetSchemas().find(schema => !!Object.keys(schema)))) {
    // there are custom widget schemas, we can't use the static validation
    // this is the current `validateConfig` function.
    // this will trigger a CSP error unless `unsafe-eval` is set.
    return dynamicValidateConfig(config)
  } else {
    return staticValidateConfig(config);
  }
}

It's a bit of a strange implicit opt-out, but it does have the advantage of being backwards compatible.

@erezrokah
Copy link
Contributor

I like that idea @cowboyd 💡 !

@dagda1
Copy link
Contributor

dagda1 commented Dec 21, 2021

@erezrokah I'm having a look at implementing @cowboyd 's suggestion.

There are a couple of places in the codebase where registerWidget is called.

One is in netlify-cms-app where a number of widgets are registered, and in netlify-cms-core where an UnknownControl is registered.

I wonder if you could help me understand if:

  1. The UnknownControl is always registered as a default editor control.
  2. Do we need to account for the widgets in netlify-cms-app to determine if the schema is a dynamic schema or not?

Thanks for your input

@erezrokah
Copy link
Contributor

Hi 👋

The UnknownControl is always registered as a default editor control.

UnknownControl is always registered, and it's in the "core" since it renders as a fallback when the CMS doesn't find a widget (e.g. someone made a typo in the configuration).
See https://github.com/netlify/netlify-cms/blob/dd8f0050c4e2d646e851da28056456c4b76a5738/packages/netlify-cms-core/src/lib/registry.js#L143

We can assume it will never have a schema.

Do we need to account for the widgets in netlify-cms-app to determine if the schema is a dynamic schema or not?

I think the schemas for those could be evaluated at build time, as netlify-cms-app should not reference widgets outside this monorepo.

dagda1 added a commit to dagda1/netlify-cms that referenced this issue Dec 22, 2021
taras pushed a commit to taras/netlify-cms that referenced this issue Jan 7, 2022
@taras
Copy link

taras commented Jan 7, 2022

@erezrokah I created a PR that includes changes implemented by @dagda1 and has all of the tests passing. There is a blocking the PR that I'll describe in a comment.

@taras
Copy link

taras commented Jan 10, 2022

With help from @dagda1, I created an example repo that has CSP with Netlify. I created a version of Netlify build that works with CSP. You can find it here https://github.com/taras/create-react-app-csp-strict-example

@taras
Copy link

taras commented Jan 10, 2022

We still need iarna/iarna-toml#45 to fully eliminate eval.

nawan95 added a commit to nawan95/nawan that referenced this issue Jan 28, 2022
@marius-mcfly
Copy link

Any updates here?

@processton
Copy link

processton commented Nov 8, 2023

I have managed to solve above issue using exclude option and allowed Unsafe Eval.
image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging a pull request may close this issue.

10 participants