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

Add DSL for configuring Content-Security-Policy header #31162

Merged
merged 1 commit into from Nov 27, 2017

Conversation

Projects
None yet
6 participants
@pixeltrix
Member

pixeltrix commented Nov 15, 2017

Pushing up to get feedback on implementation

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

Examples
Global config in an initializer:

# config/initializers/content_security_policy.rb
Rails.application.config.content_security_policy do
  p.default_src :self, :https
  p.font_src    :self, :https, :data
  p.img_src     :self, :https, :data
  p.object_src  :none
  p.script_src  :self, :https
  p.style_src   :self, :https, :unsafe_inline

  # Specify URI for violation reports
  # p.report_uri  "/csp-violation-report-endpoint"
end

# Report CSP violations to a specified URI
# For further information see the following documentation:
# https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy-Report-Only
# Rails.application.config.content_security_policy_report_only = true

Per controller configuration:

# Override policy inline
class PostsController < ApplicationController
  content_security_policy do |p|
    p.upgrade_insecure_requests true
  end
end

# Using literal values
class PostsController < ApplicationController
  content_security_policy do |p|
    p.base_uri "https://www.example.com"
  end
end

# Using mixed static and dynamic values
class PostsController < ApplicationController
  content_security_policy do |p|
    p.base_uri :self, -> { "https://#{current_user.domain}.example.com" }
  end
end
@jeremy

Lovely. Thanks for digging in to this. Added comments on 1. protect_content method naming; 2. declaring the base content security policy rather than using a boolean flag to indicate whether to use :application as the default policy; and 3. separating policy definitions from policy enforcement decisions, moving report-only out to the controller.

Also, food for thought: should the base policy be applied to all HTML responses in a Rack middleware? Catching other mounted apps, static pages, error pages, etc.

@pixeltrix

This comment has been minimized.

Show comment
Hide comment
@pixeltrix

pixeltrix Nov 16, 2017

Member

@jeremy thanks for the review - agree that we should generate the default policy for all HTML responses so will move the policy class out to Action Dispatch and then just have the override implementation in Action Controller.

Member

pixeltrix commented Nov 16, 2017

@jeremy thanks for the review - agree that we should generate the default policy for all HTML responses so will move the policy class out to Action Dispatch and then just have the override implementation in Action Controller.

@kaspth

kaspth approved these changes Nov 26, 2017

Minor nits, but overall on API 👍

Show outdated Hide outdated actionpack/lib/action_controller/metal/content_security_policy.rb Outdated
Show outdated Hide outdated actionpack/lib/action_dispatch/http/content_security_policy.rb Outdated
Show outdated Hide outdated ...rs/rails/app/templates/config/initializers/content_security_policy.rb.tt Outdated
# For further information see the following documentation
# https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy
Rails.application.config.content_security_policy do |p|

This comment has been minimized.

@kaspth

kaspth Nov 26, 2017

Member

Should this config be commented out for upgrading apps? Or should it be loose enough that apps can upgrade seamlessly and get more security?

@kaspth

kaspth Nov 26, 2017

Member

Should this config be commented out for upgrading apps? Or should it be loose enough that apps can upgrade seamlessly and get more security?

This comment has been minimized.

@pixeltrix

pixeltrix Nov 27, 2017

Member

Do initializers get generated for upgrading apps automatically?

@pixeltrix

pixeltrix Nov 27, 2017

Member

Do initializers get generated for upgrading apps automatically?

This comment has been minimized.

@kaspth

kaspth Nov 27, 2017

Member

IIRC, yes.

@kaspth

kaspth Nov 27, 2017

Member

IIRC, yes.

This comment has been minimized.

@pixeltrix

pixeltrix Nov 27, 2017

Member

Okay, will investigate what the preferred solution is to be once the beta ships and it's easier to test against upgrading real world apps.

@pixeltrix

pixeltrix Nov 27, 2017

Member

Okay, will investigate what the preferred solution is to be once the beta ships and it's easier to test against upgrading real world apps.

@pixeltrix pixeltrix changed the title from [WIP] Add DSL for configuring Content-Security-Policy header to Add DSL for configuring Content-Security-Policy header Nov 27, 2017

@pixeltrix pixeltrix merged commit afc2b42 into master Nov 27, 2017

3 checks passed

codeclimate All good!
Details
continuous-integration/travis-ci/pr The Travis CI build passed
Details
continuous-integration/travis-ci/push The Travis CI build passed
Details

@pixeltrix pixeltrix deleted the add-csp-config branch Nov 27, 2017

pixeltrix added a commit that referenced this pull request Nov 27, 2017

prathamesh-sonpatki added a commit to prathamesh-sonpatki/rails that referenced this pull request Nov 27, 2017

prathamesh-sonpatki added a commit to prathamesh-sonpatki/rails that referenced this pull request Nov 27, 2017

pixeltrix added a commit that referenced this pull request Nov 27, 2017

@dwightwatson

This comment has been minimized.

Show comment
Hide comment
@dwightwatson

dwightwatson Nov 27, 2017

Contributor

Is there a guide or any documentation on how to use this in development? For example, JavaScripts served through the asset pipeline or Webpacker (either precompiled or with the hot-reloading development server) are all HTTP and therefore left broken with the default configuration.

Contributor

dwightwatson commented Nov 27, 2017

Is there a guide or any documentation on how to use this in development? For example, JavaScripts served through the asset pipeline or Webpacker (either precompiled or with the hot-reloading development server) are all HTTP and therefore left broken with the default configuration.

@pixeltrix

This comment has been minimized.

Show comment
Hide comment
@pixeltrix

pixeltrix Nov 27, 2017

Member

@dwightwatson I thought that 'self' would allow HTTP assets served from the same domain and https: allows third party assets from HTTPS sources? I took the defaults from #24961 which is described as 'lenient'.

Member

pixeltrix commented Nov 27, 2017

@dwightwatson I thought that 'self' would allow HTTP assets served from the same domain and https: allows third party assets from HTTPS sources? I took the defaults from #24961 which is described as 'lenient'.

@dwightwatson

This comment has been minimized.

Show comment
Hide comment
@dwightwatson

dwightwatson Nov 27, 2017

Contributor

@pixeltrix That's what I thought as well, and after digging further into it that appears to be correct.

The issue I'm seeing is related but has to do with unsafe-eval. Looks like Webpacker bundles up modules using the eval method and this default CSP configuration doesn't allow for that.

Should I open another ticket to follow this issue instead? It might require the CSP configuration to be altered to allow for Webpacker assets or Webpacker might need to be configured to bundle another way.

Uncaught 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' https:".

Contributor

dwightwatson commented Nov 27, 2017

@pixeltrix That's what I thought as well, and after digging further into it that appears to be correct.

The issue I'm seeing is related but has to do with unsafe-eval. Looks like Webpacker bundles up modules using the eval method and this default CSP configuration doesn't allow for that.

Should I open another ticket to follow this issue instead? It might require the CSP configuration to be altered to allow for Webpacker assets or Webpacker might need to be configured to bundle another way.

Uncaught 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' https:".

@pixeltrix

This comment has been minimized.

Show comment
Hide comment
@pixeltrix

pixeltrix Nov 27, 2017

Member

@dwightwatson is it the webpacker gem that uses eval or webpack itself?

Member

pixeltrix commented Nov 27, 2017

@dwightwatson is it the webpacker gem that uses eval or webpack itself?

@pixeltrix

This comment has been minimized.

Show comment
Hide comment
@pixeltrix

pixeltrix Nov 27, 2017

Member

@dwightwatson I wonder whether rails/webpacker#1011 is related? Can you change config.devtool to cheap-module-source-map and see whether that fixes your problem.

Member

pixeltrix commented Nov 27, 2017

@dwightwatson I wonder whether rails/webpacker#1011 is related? Can you change config.devtool to cheap-module-source-map and see whether that fixes your problem.

@dwightwatson

This comment has been minimized.

Show comment
Hide comment
@dwightwatson

dwightwatson Nov 27, 2017

Contributor

I was just reading the same thing - yeah it looks like that configuration change fixes the issue, all working locally now. Is making that configuration the new default something that could be reconsidered?

Contributor

dwightwatson commented Nov 27, 2017

I was just reading the same thing - yeah it looks like that configuration change fixes the issue, all working locally now. Is making that configuration the new default something that could be reconsidered?

@pixeltrix

This comment has been minimized.

Show comment
Hide comment
@pixeltrix

pixeltrix Nov 27, 2017

Member

Is making that configuration the new default something that could be considered?

Possibly - will raise it in the Campfire channel.

Member

pixeltrix commented Nov 27, 2017

Is making that configuration the new default something that could be considered?

Possibly - will raise it in the Campfire channel.

@crispinheneise

This comment has been minimized.

Show comment
Hide comment
@crispinheneise

crispinheneise Nov 28, 2017

I'm also having this issue, using rack-livereload:

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

Refused to load the script 'http://localhost:35729/livereload.js?' because it violates the following Content Security Policy directive: "script-src 'self' https:".

crispinheneise commented Nov 28, 2017

I'm also having this issue, using rack-livereload:

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

Refused to load the script 'http://localhost:35729/livereload.js?' because it violates the following Content Security Policy directive: "script-src 'self' https:".

@pixeltrix

This comment has been minimized.

Show comment
Hide comment
@pixeltrix

pixeltrix Nov 28, 2017

Member

@crispinheneise the quick fix is to add :unsafe_inline to the p.script_src line in your content_security_policy.rb initializer, however you lose some of the security that the CSP gives you in preventing XSS attacks. Ideally the rack-livereload middleware would add a nonce to the script tags it injects and then modify the CSP header to add nonce-<randomvalue>. That would allow you to use live reload in development without affecting your production environment.

Member

pixeltrix commented Nov 28, 2017

@crispinheneise the quick fix is to add :unsafe_inline to the p.script_src line in your content_security_policy.rb initializer, however you lose some of the security that the CSP gives you in preventing XSS attacks. Ideally the rack-livereload middleware would add a nonce to the script tags it injects and then modify the CSP header to add nonce-<randomvalue>. That would allow you to use live reload in development without affecting your production environment.

@crispinheneise

This comment has been minimized.

Show comment
Hide comment
@crispinheneise

crispinheneise Nov 28, 2017

This may be an edge case, or it might belong to rack-livereload - if so please ignore.

In order to get CSP to work with rack-livereload I had to add unsafe-inline as @pixeltrix mentioned. Plus, I also had to add a script_src for port 35729 and a connect_src directive for the web socket. This is only needed in development, so this is what I did in content_security_policy.rb:

  if Rails.env.development?
    p.script_src  :self, :https, :unsafe_inline, 'http://localhost:35729'
    p.connect_src 'http://localhost:35729', 'ws://localhost:35729'
  else
    p.script_src  :self, :https
  end

crispinheneise commented Nov 28, 2017

This may be an edge case, or it might belong to rack-livereload - if so please ignore.

In order to get CSP to work with rack-livereload I had to add unsafe-inline as @pixeltrix mentioned. Plus, I also had to add a script_src for port 35729 and a connect_src directive for the web socket. This is only needed in development, so this is what I did in content_security_policy.rb:

  if Rails.env.development?
    p.script_src  :self, :https, :unsafe_inline, 'http://localhost:35729'
    p.connect_src 'http://localhost:35729', 'ws://localhost:35729'
  else
    p.script_src  :self, :https
  end
@pixeltrix

This comment has been minimized.

Show comment
Hide comment
@pixeltrix

pixeltrix Nov 28, 2017

Member

@crispinheneise only issue is that change is that you'll only find out about your own app's use of inline scripts when you deploy to production 😟

Member

pixeltrix commented Nov 28, 2017

@crispinheneise only issue is that change is that you'll only find out about your own app's use of inline scripts when you deploy to production 😟

@nsingh

This comment has been minimized.

Show comment
Hide comment
@nsingh

nsingh Jun 14, 2018

Hi @pixeltrix ,

Thanks for implementing CSP in Rails! We are still on Rails 5.0.6 so we had to backport your code to get things in motion.

I did have one question regarding the ability to set both headers:

POLICY = "Content-Security-Policy".freeze
POLICY_REPORT_ONLY = "Content-Security-Policy-Report-Only".freeze

Currently it seems like if Report-Only option is true, it takes precedence:-

def header_name(request)
  if request.content_security_policy_report_only
    POLICY_REPORT_ONLY
  else
    POLICY
  end
end

The CSP documentation says that both headers can be present in the same response and could have different policies.

I am wondering if this was deliberate and whether Rails has plans to honor both headers in the near future?

nsingh commented Jun 14, 2018

Hi @pixeltrix ,

Thanks for implementing CSP in Rails! We are still on Rails 5.0.6 so we had to backport your code to get things in motion.

I did have one question regarding the ability to set both headers:

POLICY = "Content-Security-Policy".freeze
POLICY_REPORT_ONLY = "Content-Security-Policy-Report-Only".freeze

Currently it seems like if Report-Only option is true, it takes precedence:-

def header_name(request)
  if request.content_security_policy_report_only
    POLICY_REPORT_ONLY
  else
    POLICY
  end
end

The CSP documentation says that both headers can be present in the same response and could have different policies.

I am wondering if this was deliberate and whether Rails has plans to honor both headers in the near future?

jacobbednarz added a commit to jacobbednarz/rails that referenced this pull request Jul 26, 2018

Adds support for configuring HTTP Feature Policy
A HTTP feature policy is Yet Another HTTP header for instructing the
browser about which features the application intends to make use of and
to lock down access to others. This is a new security mechanism that
ensures that should an application become compromised or a third party
attempts an unexpected action, the browser will override it and maintain
the intended UX.

WICG specification: https://wicg.github.io/feature-policy/

The end result is a HTTP header that looks like the following:

```
Feature-Policy: geolocation 'none'; autoplay https://example.com
```

This will prevent the browser from using geolocation and only allow
autoplay on `https://example.com`. Full feature list can be found over
in the WICG repository[1].

As of today Chrome and Safari have public support[2] for this
functionality with Firefox working on support[3] and Edge still pending
acceptance of the suggestion[4].

#### Examples

Using an initializer

```rb
# config/initializers/feature_policy.rb
Rails.application.config.feature_policy do |f|
  f.geolocation :none
  f.camera      :none
  f.payment     "https://secure.example.com"
  f.fullscreen  :self
end
```

In a controller

```rb
class SampleController < ApplicationController
  def index
    feature_policy do |f|
      f.geolocation "https://example.com"
    end
  end
end
```

Some of you might realise that the HTTP feature policy looks pretty
close to that of a Content Security Policy; and you're right. So much so
that I used the Content Security Policy DSL from rails#31162 as the starting
point for this change.

This change *doesn't* introduce support for defining a feature policy on
an iframe and this has been intentionally done to split the HTTP header
and the HTML element (`iframe`) support. If this is successful, I'll
look to add that on it's own.

Full documentation on HTTP feature policies can be found at
https://wicg.github.io/feature-policy/. Google have also published[5] a
great in-depth write up of this functionality.

[1]: https://github.com/WICG/feature-policy/blob/master/features.md
[2]: https://www.chromestatus.com/feature/5694225681219584
[3]: https://bugzilla.mozilla.org/show_bug.cgi?id=1390801
[4]: https://wpdev.uservoice.com/forums/257854-microsoft-edge-developer/suggestions/33507907-support-feature-policy
[5]: https://developers.google.com/web/updates/2018/06/feature-policy
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment