Open oreoshake opened 3 years ago
@oreoshake I thought it worth mentioning that I just opened rails/rails#46859, which allows modification of Rails' global CSP. It is a much simpler implementation than what you describe in your comment above, but it's what I've been using in some projects via a monkey patch, and has worked reasonably well!
That’s wonderful @agrobbin!
This is really good work! Thank you :)
This is more of a meta issue where I'm going to drop notes before actually coming up with a documented plan.
We intend to go through this process in the not so distant future and will have lessons to learn. GitHub uses the following features:
append_content_security_policy_directives
on a value that is configured as'none'
https://bauland42.com/ruby-on-rails-content-security-policy-csp/ does a good job of explaining both APIs. https://medium.com/just-tech/content-security-policy-ruby-on-rails-836d0c7ba098j is a story of someone doing exactly this (and calling out one important difference.
base config
Creating your base config from the secure_headers config is mostly a find/replace. Instead of a hash, you're configuring an object with methods named after directives. They take varargs so the arrays need to be splatted.
dynamic modifications
As mentioned in https://bauland42.com/ruby-on-rails-content-security-policy-csp/, the rails API overwrites config values. In secure_headers land, we generally append sources to an existing list rather than have to redefine the entire list upon use.
So the obvious workaround is to grab the current config and then re-set the value along with additions. This shim seems to generally solve the problem while backporting some functionality, notably:
append_content_security_policy_directives
on a currently undefined directive, populate the newly defined directive with the current value ofdefault_src
append_content_security_policy_directives
on a directive with the value ofnone
, discard thenone
valuetesting
By default, rails does not include middleware when running tests. If you're testing your CSP (hint: you should) then you've already done this in your test class:
differing policies
If you're testing your policies (seriously: do it) then you won't get exact matches without some extra steps.
Rails won't dedup anything so you'll be calling
uniq
if you care. Rails doesn't likenil
s in config values,secure_headers
doesn't mind and will filter them. secure_headers tries to do fancy stuff around consolidating*
or overlapping config values, rails is more literal. secure_headers omits schemes by default, rails does what you tell it to.secure_headers generates a policy that is
default_src ... everything else ... report_uri
whereas rails doesn't enforce this order.config values that won't translate
The
preserve_schemes
/disable_nonce_backwards_compatibility
concepts do not exist in rails.secure_headers with CSP disabled is somewhat of an edge case (for me)
I've never done it but while I was working on this, a test of ours that mucks with 1st X-Frame-Options raised an error about default_src not being configured 😆 thar be dragons.
non-html resources don't get CSP
I'll add details, but there's a situation where you need to send csp on PDF resources and you may want to for e.g. api requests.
And more
I'll keep updating this issue body as we roll out the change and I'd love to incorporate anything you've learned.