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

Strict Content Security Policy breaks web-console #242

Closed
renchap opened this issue May 28, 2017 · 7 comments · Fixed by #296
Closed

Strict Content Security Policy breaks web-console #242

renchap opened this issue May 28, 2017 · 7 comments · Fixed by #296

Comments

@renchap
Copy link

renchap commented May 28, 2017

For more informations and context about CSP: https://csp.withgoogle.com/docs/strict-csp.html

I have a quite strict CSP header on my website, using the secureheaders gem:

default-src 'self'; base-uri 'self'; block-all-mixed-content; child-src 'self' blob: localhost:8080; connect-src 'self' ws://localhost:8080 http://localhost:8080 ws://localhost:3000; font-src 'self' data: localhost:8080; form-action 'self'; frame-ancestors 'none'; img-src 'self' www.google-analytics.com data: blob: localhost:8080; object-src 'none'; script-src 'self' www.google-analytics.com localhost:8080 'nonce-devOnly' 'unsafe-eval'; style-src 'self' fonts.googleapis.com 'nonce-devOnly'

On Rails error page, the CSP header is sent and this breaks web-console because of inline script and style.

I do not want to remove my CSP policy in development, as it allows me to catch errors that would occur in production.

I see multiple ways to fix this:

  • web-console scripts and styles are moved to external files served with other Rails assets, so they are allowed by the policy (does not seem easy to do)
  • There is a way to configure web-console with a nonce, and the nonce is added to the inline <script> & <style> tags, and all CSS is moved to <style> tags. This is the cleaner solution, but might require quite a lot of work.
  • If the secureheaders gem is installed and web-console will be displayed, then it can call append_content_security_policy_directives to allow inline styles/scripts (see https://github.com/twitter/secureheaders/blob/master/docs/per_action_configuration.md). This is not ideal as it means the CSP policy is changed for this page and might no longer catch errors that would occur in production, but web-console should not be a permanent part of this page.
    This does not seem ideal to add secureheaders specific code in web-console, so this cna be implemented with a configurable callback before the console is displayed and the developer can call append_content_security_policy_directives from it.

Any thoughts / preferences / advice?

@gsamokovarov
Copy link
Collaborator

I like the way we currently inject the console, as it's quite portable. It does not depend on the asset pipeline and we can show it mostly everywhere without issues. Your case being the exception here.

For sure, I would like to support it, if it's not too disruptive to what we currently have. Can you explain the second option you proposed in a bit more detail? I don't think I want to pursue either 1 or 3.

@renchap
Copy link
Author

renchap commented May 28, 2017

Sure!

With CSP, you can specify a nonce in your CSP header. In my policy above, this is the nonce-devOnly part in script-src and style-src.

It means that <script> and <style> tags are allowed if they specify this nonce, even if they are not from any other authorised origin, as long as they have a nonce="devOnly attribute. This is true for inline scripts/style, so you can still use:

<style nonce="devOnly">
  body { background-color: red; }
</style>

<script nonce="devOnly">
  console.log('I am authorized')
</script>

The requirement for web-console to work with this is to move all style="..." attributes to one (or more) inline <style> tags, with the nonce attribute if provided by the developer. Not using style="..." attributes is also overall better for code organisation.

The same is required for javascript, <script> tags needs the nonce attribute. As callback HTML attributes are no longer allowed (<div onclick="...">), they need to be moved to addEventListener() calls. This is also often viewed as a best practice.

From what I saw in the source code of an error page in my rails app, this is needed:

  • provide an method to set a nonce parameter (in an initializer for example). It should accept a block to allow for dynamic nonces
  • add the nonce attribute to the <script> and <style> tags
  • move inline CSS (mostly display: block|none) to classes, and change code to toggle the classes and not the inline CSS
  • move a few onclick attributes to addEventListener()

I am not familiar at all with web-console code, but I am with CSP and I can help you if needed.

If this is too much work (and too few benefits), the simplest implementation would be to allow the developer to configure a block that will be run when web-console is displayed on a page, as I suggested above in (3). For example, in an initializer:

WebConsole.before_display do
  # Here I can change the CSP policy for this request to allow inline JS / Style
end

This is more like a workaround, but it would work for 99% of the usecases.

CSP support is now in all major browsers and a lot of websites are starting to implement it with strict policies. There have been talks to include CSP support directly into Rails to improve baseline security (but probably not with a strict default policy).

@gsamokovarov
Copy link
Collaborator

gsamokovarov commented May 28, 2017

Looking at the spec, this nonce should be unique across requests, right? Secure Headers seems to set per-request nonce in a place where we can get it, so you can still set it as the web-console one if we had exposed a way for you to control it.

That said, the above may not work, because of the way the Rails error pages are setup. The console itself is just injected on pages, the default error page is not that exceptional. Its content, however does have a couple of script tags. Now, providing a way to set those internal nonces and setting it in Rails proper may be a bit more disruptive.

@renchap
Copy link
Author

renchap commented May 28, 2017

It looks like I indeed made a mistake and most of the work needs to be done on Rails error page. I thought it was generated by web-console, my bad. I will open a ticket with Rails on the topic.

For the nonce, it should be unique per request in production. This is why the configuration for the nonce should accept a block, so you can dynamically provide the nonce.

In my case, I am using a static nonce in development, and none in production. I am using external Hot Module Reloading via Webpacker (in dev only) and I have not found a way to share a dynamic nonce between webpack-dev-server and Rails.

I guess if Rails implements CSP support on the error page there will be a Rails-way to set the nonce, and you can then reuse it in web-console

@guilleiguaran
Copy link
Member

This seems relevant again now that Rails includes a CSP middleware by default.

@ansonhoyt
Copy link

I think a nonce approach might be very straightforward. Putting this out there, as it could be an easy pull request, but I don't have time to land it right now.

What Rails provides:

I think we can assume users would configure Rails' CSP to generate a nonce, as per the Securing Rails Applications guide.

Rails provides csp_meta_tag

which returns a meta tag “csp-nonce” with the per-session nonce value for allowing inline <script> tags.
This is used by the Rails UJS helper to create dynamically loaded inline <script> elements.

Looking at rails-ujs, it defines Rails.loadCSPNonce which reads this "csp-nonce" meta tag.

The nonce also appears to be available in request.content_security_policy_nonce, which looks rather convenient.

What web-console needs:

If a nonce is available in either the request or the "csp-nonce" meta tag, web-console just needs to add the value to each inline <script> and <style> tag.

@jtokoph
Copy link

jtokoph commented Apr 14, 2020

For those that Google brings: I worked around this with the secure-headers gem by including this in my ApplicationController:

if Rails.env.development? && defined? WebConsole
  rescue_from StandardError do |exception|
    append_content_security_policy_directives(script_src: %w['unsafe-inline'])
    raise exception
  end
end

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

Successfully merging a pull request may close this issue.

5 participants