decap-cms: CMS does not work with Content Security Policy (CSP). Requires unsafe-eval / unsafe-inline for script-src / style-src
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
About this issue
- Original URL
- State: open
- Created 5 years ago
- Reactions: 9
- Comments: 21 (6 by maintainers)
Commits related to this issue
- fix(csp): update ol and fix webpack type (#2138) — committed to thefrontside/netlify-cms by dagda1 3 years ago
- fix(csp): update ol and fix webpack type (#2138) — committed to thefrontside/netlify-cms by dagda1 3 years ago
- fix(csp): add height prop to withMapControl widget (#2138) — committed to thefrontside/netlify-cms by dagda1 3 years ago
- fix(csp): add height prop to withMapControl widget (#2138) — committed to thefrontside/netlify-cms by dagda1 3 years ago
- fix(csp): update svgo babel.config to use presetDefaults instead of extendDefaultPlugins (#2138) — committed to thefrontside/netlify-cms by dagda1 3 years ago
- fix(csp): update svgo babel.config to use presetDefaults instead of extendDefaultPlugins (#2138) — committed to thefrontside/netlify-cms by dagda1 3 years ago
- fix(csp): update svgo babel.config to use presetDefaults instead of extendDefaultPlugins (#2138) — committed to thefrontside/netlify-cms by dagda1 3 years ago
- fix(csp): update svgo babel.config to use presetDefaults instead of extendDefaultPlugins (#2138) — committed to thefrontside/netlify-cms by dagda1 3 years ago
- fix(csp): update ol and fix webpack type (#2138) — committed to thefrontside/netlify-cms by dagda1 3 years ago
- fix(csp): add height prop to withMapControl widget (#2138) — committed to thefrontside/netlify-cms by dagda1 3 years ago
- fix(csp): update svgo babel.config to use presetDefaults instead of extendDefaultPlugins (#2138) — committed to thefrontside/netlify-cms by dagda1 3 years ago
- fix(csp): add height prop to withMapControl widget (#2138) — committed to thefrontside/netlify-cms by dagda1 3 years ago
- fix(csp): change validateConfig to implicity opt in or out of dynamic ajv validation (#2138) — committed to dagda1/netlify-cms by dagda1 3 years ago
- fix(csp): change validateConfig to implicity opt in or out of dynamic ajv validation (#2138) — committed to taras/netlify-cms by dagda1 3 years ago
- Remove Netlify CMS https://github.com/netlify/netlify-cms/issues/2138#issue-416815976 — committed to nawan95/nawan by nawan95 2 years ago
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.
It’s a bit of a strange implicit opt-out, but it does have the advantage of being backwards compatible.
@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 viaregisterWidget()
https://github.com/netlify/netlify-cms/blob/2877f7983e9b5f4ff0cb2cdaaf6549675af68913/packages/netlify-cms-core/src/constants/configSchema.js#L75Unfortunately, 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.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.
Any updates here?
I like that idea @cowboyd 💡 !