Merge branch 'master' into enforce_lambda
Conflicts: fixtures/rails_3_2_12/spec/controllers/other_things_controller_spec.rb lib/secure_headers/headers/content_security_policy.rb
This commit is contained in:
Коммит
85b923c310
31
README.md
31
README.md
|
@ -197,9 +197,36 @@ and [Mozilla CSP specification](https://wiki.mozilla.org/Security/CSP/Specificat
|
|||
"default-src 'self'; img-src *; object-src media1.com media2.com *.cdn.com; script-src trustedscripts.example.com;"
|
||||
```
|
||||
|
||||
## Note on Firefox handling of CSP
|
||||
### CSP Level 2 features
|
||||
|
||||
Currently, Firefox does not support the w3c draft standard. So there are a few steps taken to make the two interchangeable.
|
||||
script/style-nonce can be used to whitelist inline content. To do this, add "nonce" to your script/style-src configuration, then set the nonce attributes on the various tags.
|
||||
|
||||
*setting a nonce will also set 'unsafe-inline' for browsers that don't support nonces for backwards compatibility. 'unsafe-inline' is ignored if a nonce is present in a directive in compliant browsers.
|
||||
|
||||
```ruby
|
||||
:csp => {
|
||||
:default_src => 'self',
|
||||
:script_src => 'self nonce'
|
||||
}
|
||||
```
|
||||
|
||||
> content-security-policy: default-src 'self'; script-src 'self' 'nonce-abc123' 'unsafe-inline'
|
||||
|
||||
```erb
|
||||
<script nonce="<%= @content_security_policy_nonce %>">
|
||||
console.log("whitelisted, will execute")
|
||||
</script>
|
||||
|
||||
<script nonce="lol">
|
||||
console.log("won't execute, not whitelisted")
|
||||
</script>
|
||||
|
||||
<script>
|
||||
console.log("won't execute, not whitelisted")
|
||||
</script>
|
||||
```
|
||||
|
||||
## Note on Firefox handling of CSP
|
||||
|
||||
* CSP reports will not POST cross\-origin. This sets up an internal endpoint in the application that will forward the request. Set the `forward_endpoint` value in the CSP section if you need to post cross origin for firefox. The internal endpoint that receives the initial request will forward the request to `forward_endpoint`
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
config.x_xss_protection = {:value => 1, :mode => 'block'}
|
||||
csp = {
|
||||
:default_src => "self",
|
||||
:script_src => "self nonce",
|
||||
:disable_chrome_extension => true,
|
||||
:disable_fill_missing => true,
|
||||
:report_uri => 'somewhere',
|
||||
|
|
|
@ -12,12 +12,13 @@ describe OtherThingsController, :type => :controller do
|
|||
expect(response.headers['X-Frame-Options']).to eq('SAMEORIGIN')
|
||||
end
|
||||
|
||||
it "sets the X-WebKit-CSP header" do
|
||||
it "sets the CSP header with a local reference to a nonce" do
|
||||
get :index
|
||||
expect(response.headers['Content-Security-Policy-Report-Only']).to eq("default-src 'self'; img-src 'self' data:; report-uri somewhere;")
|
||||
nonce = controller.instance_exec { @content_security_policy_nonce }
|
||||
expect(nonce).to match /[a-zA-Z0-9\+\/=]{44}/
|
||||
expect(response.headers['Content-Security-Policy-Report-Only']).to match(/default-src 'self'; img-src 'self' data:; script-src 'self' 'nonce-[a-zA-Z0-9\+\/=]{44}' 'unsafe-inline'; report-uri somewhere;/)
|
||||
end
|
||||
|
||||
#mock ssl
|
||||
it "sets the Strict-Transport-Security header" do
|
||||
request.env['HTTPS'] = 'on'
|
||||
get :index
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
require 'uri'
|
||||
require 'base64'
|
||||
|
||||
module SecureHeaders
|
||||
class ContentSecurityPolicyBuildError < StandardError; end
|
||||
|
@ -30,6 +31,7 @@ module SecureHeaders
|
|||
def initialize(config=nil, options={})
|
||||
@experimental = !!options.delete(:experimental)
|
||||
@controller = options.delete(:controller)
|
||||
|
||||
if options[:request]
|
||||
parse_request(options[:request])
|
||||
else
|
||||
|
@ -45,6 +47,10 @@ module SecureHeaders
|
|||
configure(config) if config
|
||||
end
|
||||
|
||||
def nonce
|
||||
@nonce ||= SecureRandom.base64(32).chomp
|
||||
end
|
||||
|
||||
def configure(config)
|
||||
@config = config.dup
|
||||
|
||||
|
@ -125,10 +131,10 @@ module SecureHeaders
|
|||
# space-delimeted strings
|
||||
config_val = config_val.split if config_val.is_a? String
|
||||
# array of strings
|
||||
if config_val.respond_to?(:map)
|
||||
if config_val.respond_to?(:map) #skip booleans
|
||||
config_val = config_val.map do |val|
|
||||
translate_dir_value(val)
|
||||
end
|
||||
end.flatten.uniq
|
||||
end
|
||||
|
||||
hash[key] = config_val
|
||||
|
@ -145,6 +151,9 @@ module SecureHeaders
|
|||
# self/none are special sources/src-dir-values and need to be quoted in chrome
|
||||
elsif %{self none}.include?(val)
|
||||
"'#{val}'"
|
||||
elsif val == 'nonce'
|
||||
@controller.instance_variable_set(:@content_security_policy_nonce, nonce)
|
||||
["'nonce-#{nonce}'", "'unsafe-inline'"]
|
||||
else
|
||||
val
|
||||
end
|
||||
|
|
|
@ -262,6 +262,31 @@ module SecureHeaders
|
|||
end
|
||||
end
|
||||
|
||||
context "when using a nonce" do
|
||||
it "adds a nonce and unsafe-inline to the script-src value" do
|
||||
header = ContentSecurityPolicy.new(default_opts.merge(:script_src => "self nonce"), :request => request_for(CHROME))
|
||||
expect(header.value).to include("script-src 'self' 'nonce-#{header.nonce}' 'unsafe-inline'")
|
||||
end
|
||||
|
||||
it "adds a nonce and unsafe-inline to the style-src value" do
|
||||
header = ContentSecurityPolicy.new(default_opts.merge(:style_src => "self nonce"), :request => request_for(CHROME))
|
||||
expect(header.value).to include("style-src 'self' 'nonce-#{header.nonce}' 'unsafe-inline'")
|
||||
end
|
||||
|
||||
it "adds an identical nonce to the style and script-src directives" do
|
||||
header = ContentSecurityPolicy.new(default_opts.merge(:style_src => "self nonce", :script_src => "self nonce"), :request => request_for(CHROME))
|
||||
nonce = header.nonce
|
||||
value = header.value
|
||||
expect(value).to include("style-src 'self' 'nonce-#{nonce}' 'unsafe-inline'")
|
||||
expect(value).to include("script-src 'self' 'nonce-#{nonce}' 'unsafe-inline'")
|
||||
end
|
||||
|
||||
it "does not add 'unsafe-inline' twice" do
|
||||
header = ContentSecurityPolicy.new(default_opts.merge(:script_src => "self nonce inline"), :request => request_for(CHROME))
|
||||
expect(header.value).to include("script-src 'self' 'nonce-#{header.nonce}' 'unsafe-inline';")
|
||||
end
|
||||
end
|
||||
|
||||
context "when supplying a experimental values" do
|
||||
let(:options) {{
|
||||
:disable_chrome_extension => true,
|
||||
|
|
Загрузка…
Ссылка в новой задаче