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:
Neil Matatall 2014-08-08 15:09:50 -07:00
Родитель 619ff5e6f2 325867fea9
Коммит 85b923c310
5 изменённых файлов: 70 добавлений и 7 удалений

Просмотреть файл

@ -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,