secure_headers 1.4.1 → 2.0.0.pre
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of secure_headers might be problematic. Click here for more details.
- checksums.yaml +7 -0
- data/.gitignore +4 -8
- data/Gemfile +2 -2
- data/Guardfile +8 -0
- data/README.md +102 -48
- data/Rakefile +0 -116
- data/fixtures/rails_3_2_12/app/views/layouts/application.html.erb +1 -1
- data/fixtures/rails_3_2_12/app/views/other_things/index.html.erb +2 -1
- data/fixtures/rails_3_2_12/config/initializers/secure_headers.rb +1 -1
- data/fixtures/rails_3_2_12/config/script_hashes.yml +5 -0
- data/fixtures/rails_3_2_12/config.ru +3 -0
- data/fixtures/rails_3_2_12/spec/controllers/other_things_controller_spec.rb +50 -18
- data/fixtures/rails_3_2_12/spec/controllers/things_controller_spec.rb +1 -1
- data/fixtures/rails_3_2_12_no_init/app/controllers/other_things_controller.rb +1 -2
- data/lib/secure_headers/hash_helper.rb +7 -0
- data/lib/secure_headers/headers/content_security_policy/script_hash_middleware.rb +22 -0
- data/lib/secure_headers/headers/content_security_policy.rb +141 -137
- data/lib/secure_headers/railtie.rb +0 -22
- data/lib/secure_headers/version.rb +1 -1
- data/lib/secure_headers/view_helper.rb +68 -0
- data/lib/secure_headers.rb +51 -17
- data/lib/tasks/tasks.rake +48 -0
- data/spec/lib/secure_headers/headers/content_security_policy/script_hash_middleware_spec.rb +47 -0
- data/spec/lib/secure_headers/headers/content_security_policy_spec.rb +83 -208
- data/spec/lib/secure_headers_spec.rb +16 -62
- data/spec/spec_helper.rb +25 -1
- metadata +22 -24
- data/HISTORY.md +0 -162
- data/app/controllers/content_security_policy_controller.rb +0 -76
- data/config/curl-ca-bundle.crt +0 -5420
- data/config/routes.rb +0 -3
- data/spec/controllers/content_security_policy_controller_spec.rb +0 -90
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 4d41b253fa38de884f864558563330b9e707e2dc
|
4
|
+
data.tar.gz: 69b19dbc4ab124481e6f47dad73cd7a6497aa844
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: fa7e778e7cb305a3273d4b3aa94bf8597972910da752b218ebf0779330c4c025c2906cec3974a410193db600fc899465a2da8bc7a8ca5720eb30ba95335e7a5d
|
7
|
+
data.tar.gz: 21934fb364794d3fc08e7dde201e04cb1253686e362e46ec370b06021007cb897fc3182de9a1ac646fa2448f4ce7a9f45b33da31f4d2a8690b76c42527bb9001
|
data/.gitignore
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
*.gem
|
2
|
+
*.DS_STORE
|
2
3
|
*.rbc
|
3
4
|
.bundle
|
4
5
|
.config
|
@@ -15,12 +16,7 @@ rdoc
|
|
15
16
|
spec/reports
|
16
17
|
test/tmp
|
17
18
|
test/version_tmp
|
18
|
-
tmp
|
19
|
-
|
20
|
-
/fixtures/rails_3_2_12/log/test.log
|
21
|
-
/fixtures/rails_3_2_12_no_init/log/test.log
|
19
|
+
*tmp
|
22
20
|
*.sqlite3
|
23
|
-
|
24
|
-
|
25
|
-
/fixtures/rails_3_2_12_no_init/db/test.sqlite3
|
26
|
-
/fixtures/rails_3_2_12/db/test.sqlite3
|
21
|
+
fixtures/rails_3_2_12_no_init/log
|
22
|
+
fixtures/rails_3_2_12/log
|
data/Gemfile
CHANGED
@@ -8,8 +8,8 @@ group :test do
|
|
8
8
|
gem 'jdbc-sqlite3', :platform => :jruby
|
9
9
|
gem 'rspec-rails', '>= 3.1'
|
10
10
|
gem 'rspec', '>= 3.1'
|
11
|
+
gem 'guard-rspec', :platform => [:ruby_19, :ruby_20, :ruby_21]
|
11
12
|
gem 'growl'
|
12
13
|
gem 'rb-fsevent'
|
13
|
-
gem '
|
14
|
-
gem 'ruby-debug', :platform => :ruby_18
|
14
|
+
gem 'coveralls', :platform => :ruby_19
|
15
15
|
end
|
data/Guardfile
ADDED
@@ -0,0 +1,8 @@
|
|
1
|
+
notification :growl
|
2
|
+
|
3
|
+
guard 'rspec', cmd: 'rspec' do
|
4
|
+
watch(%r{^spec/.+_spec\.rb$})
|
5
|
+
watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
|
6
|
+
watch(%r{^app/controllers/(.+)\.rb$}) { |m| "spec/controllers/#{m[1]}_spec.rb" }
|
7
|
+
watch('spec/spec_helper.rb') { "spec" }
|
8
|
+
end
|
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# SecureHeaders [![Build Status](https://travis-ci.org/twitter/secureheaders.png?branch=master)](http://travis-ci.org/twitter/secureheaders) [![Code Climate](https://codeclimate.com/github/twitter/secureheaders.png)](https://codeclimate.com/github/twitter/secureheaders)
|
1
|
+
# SecureHeaders [![Build Status](https://travis-ci.org/twitter/secureheaders.png?branch=master)](http://travis-ci.org/twitter/secureheaders) [![Code Climate](https://codeclimate.com/github/twitter/secureheaders.png)](https://codeclimate.com/github/twitter/secureheaders) [![Coverage Status](https://coveralls.io/repos/twitter/secureheaders/badge.png)](https://coveralls.io/r/twitter/secureheaders)
|
2
2
|
|
3
3
|
The gem will automatically apply several headers that are related to security. This includes:
|
4
4
|
- Content Security Policy (CSP) - Helps detect/prevent XSS, mixed-content, and other classes of attack. [CSP 1.1 Specification](https://dvcs.w3.org/hg/content-security-policy/raw-file/tip/csp-specification.dev.html)
|
@@ -54,8 +54,6 @@ The following methods are going to be called, unless they are provided in a `ski
|
|
54
54
|
This gem makes a few assumptions about how you will use some features. For example:
|
55
55
|
|
56
56
|
* It fills any blank directives with the value in `:default_src` Getting a default\-src report is pretty useless. This way, you will always know what type of violation occurred. You can disable this feature by supplying `:disable_fill_missing => true`. This is referred to as the "effective-directive" in the spec, but is not well supported as of Nov 5, 2013.
|
57
|
-
* Firefox does not support cross\-origin CSP reports. If we are using Firefox, AND the value for `:report_uri` does not satisfy the same\-origin requirements, we will instead forward to an internal endpoint (`FF_CSP_ENDPOINT`). This is also the case if `:report_uri` only contains a path, which we assume will be cross host. This endpoint will in turn forward the request to the value in `:forward_endpoint` without restriction. More information can be found in the "Note on Firefox handling of CSP" section.
|
58
|
-
|
59
57
|
|
60
58
|
## Configuration
|
61
59
|
|
@@ -68,9 +66,9 @@ This gem makes a few assumptions about how you will use some features. For exam
|
|
68
66
|
config.x_content_type_options = "nosniff"
|
69
67
|
config.x_xss_protection = {:value => 1, :mode => 'block'}
|
70
68
|
config.csp = {
|
71
|
-
:default_src => "https
|
72
|
-
:frame_src => "https
|
73
|
-
:img_src => "https
|
69
|
+
:default_src => "https: self",
|
70
|
+
:frame_src => "https: http:.twimg.com http://itunes.apple.com",
|
71
|
+
:img_src => "https:",
|
74
72
|
:report_uri => '//example.com/uri-directive'
|
75
73
|
}
|
76
74
|
end
|
@@ -125,11 +123,6 @@ and [Mozilla CSP specification](https://wiki.mozilla.org/Security/CSP/Specificat
|
|
125
123
|
# Where reports are sent. Use protocol relative URLs if you are posting to the same domain (TLD+1). Use paths if you are posting to the application serving the header
|
126
124
|
:report_uri => '//mysite.example.com',
|
127
125
|
|
128
|
-
# Send reports that cannot be sent across host here. These requests are sent
|
129
|
-
# the server, not the browser. If no value is supplied, it will default to
|
130
|
-
# the value in report_uri. Use this if you cannot use relative protocols mentioned above due to host mismatches.
|
131
|
-
:forward_endpoint => 'https://internal.mylogaggregator.example.com'
|
132
|
-
|
133
126
|
# these directives all take 'none', 'self', or a globbed pattern
|
134
127
|
:img_src => nil,
|
135
128
|
:frame_src => nil,
|
@@ -144,25 +137,12 @@ and [Mozilla CSP specification](https://wiki.mozilla.org/Security/CSP/Specificat
|
|
144
137
|
# over http, relaxing the policy
|
145
138
|
# e.g.
|
146
139
|
# :csp => {
|
147
|
-
# :img_src => 'https
|
148
|
-
# :http_additions => {:img_src => 'http
|
140
|
+
# :img_src => 'https:',
|
141
|
+
# :http_additions => {:img_src => 'http'}
|
149
142
|
# }
|
150
|
-
# would produce the directive: "img-src https
|
143
|
+
# would produce the directive: "img-src https: http:;"
|
151
144
|
# when over http, ignored for https requests
|
152
145
|
:http_additions => {}
|
153
|
-
|
154
|
-
# If you have enforce => true, you can use the `experiments` block to
|
155
|
-
# also produce a report-only header. Values in this block override the
|
156
|
-
# parent config for the report-only, and leave the enforcing header
|
157
|
-
# unaltered. http_additions work the same way described above, but
|
158
|
-
# are added to your report-only header as expected.
|
159
|
-
:experimental => {
|
160
|
-
:script_src => 'self',
|
161
|
-
:img_src => 'https://mycdn.example.com',
|
162
|
-
:http_additions {
|
163
|
-
:img_src => 'http://mycdn.example.com'
|
164
|
-
}
|
165
|
-
}
|
166
146
|
}
|
167
147
|
```
|
168
148
|
|
@@ -172,19 +152,19 @@ and [Mozilla CSP specification](https://wiki.mozilla.org/Security/CSP/Specificat
|
|
172
152
|
```ruby
|
173
153
|
# most basic example
|
174
154
|
:csp => {
|
175
|
-
:default_src => "https
|
155
|
+
:default_src => "https: inline eval",
|
176
156
|
:report_uri => '/uri-directive'
|
177
157
|
}
|
178
158
|
|
179
|
-
> "default-src 'unsafe-inline' 'unsafe-eval' https
|
159
|
+
> "default-src 'unsafe-inline' 'unsafe-eval' https:; report-uri /uri-directive;"
|
180
160
|
|
181
161
|
# turn off inline scripting/eval
|
182
162
|
:csp => {
|
183
|
-
:default_src => 'https
|
163
|
+
:default_src => 'https:',
|
184
164
|
:report_uri => '/uri-directive'
|
185
165
|
}
|
186
166
|
|
187
|
-
> "default-src https
|
167
|
+
> "default-src https:; report-uri /uri-directive;"
|
188
168
|
|
189
169
|
# Auction site wants to allow images from anywhere, plugin content from a list of trusted media providers (including a content distribution network), and scripts only from its server hosting sanitized JavaScript
|
190
170
|
:csp => {
|
@@ -217,9 +197,13 @@ report-uri csp_reports?enforce=true&app_name=twitter
|
|
217
197
|
|
218
198
|
### CSP Level 2 features
|
219
199
|
|
200
|
+
*NOTE: Currently, only erb is supported. Mustache support isn't far off. Hash sources are valid for inline style blocks but are not yet supported by secure_headers.*
|
201
|
+
|
202
|
+
#### Nonce
|
203
|
+
|
220
204
|
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.
|
221
205
|
|
222
|
-
|
206
|
+
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.
|
223
207
|
|
224
208
|
```ruby
|
225
209
|
:csp => {
|
@@ -243,29 +227,99 @@ script/style-nonce can be used to whitelist inline content. To do this, add "non
|
|
243
227
|
console.log("won't execute, not whitelisted")
|
244
228
|
</script>
|
245
229
|
```
|
230
|
+
You can use a view helper to automatically add nonces to script tags:
|
231
|
+
```erb
|
232
|
+
<%= nonced_javascript_tag do %>
|
233
|
+
console.log("nonced!")
|
234
|
+
<% end %>
|
235
|
+
<%= nonced_javascript_tag("nonced without a block!") %>
|
236
|
+
```
|
246
237
|
|
247
|
-
|
238
|
+
becomes:
|
248
239
|
|
249
|
-
|
240
|
+
```html
|
241
|
+
<script nonce="/jRAxuLJsDXAxqhNBB7gg7h55KETtDQBXe4ZL+xIXwI=">
|
242
|
+
console.log("nonced!")
|
243
|
+
</script>
|
244
|
+
```
|
245
|
+
|
246
|
+
#### Hash
|
250
247
|
|
251
|
-
|
248
|
+
setting hash source values will also set 'unsafe-inline' for browsers that don't support hash sources for backwards compatibility. 'unsafe-inline' is ignored if a hash is present in a directive in compliant browsers.
|
252
249
|
|
253
|
-
|
254
|
-
**This is an unauthenticated, unauthorized endpoint. Only do this if your report\-uri is not on the same origin as your application!!!**
|
250
|
+
Hash source support works by taking the hash value of the contents of an inline script block and adding the hash "fingerprint" to the CSP header.
|
255
251
|
|
256
|
-
|
252
|
+
If you only have a few hashes, you can hardcode them for the entire app:
|
257
253
|
|
258
254
|
```ruby
|
259
|
-
|
255
|
+
config.csp = {
|
256
|
+
:default_src => "https:",
|
257
|
+
:script_src => 'self'
|
258
|
+
:script_hashes => ['sha1-abc', 'sha1-qwe']
|
259
|
+
}
|
260
260
|
```
|
261
261
|
|
262
|
-
|
262
|
+
The following will work as well, but may not be as clear:
|
263
|
+
|
264
|
+
```ruby
|
265
|
+
config.csp = {
|
266
|
+
:default_src => "https:",
|
267
|
+
:script_src => "self 'sha1-qwe'"
|
268
|
+
}
|
269
|
+
```
|
263
270
|
|
264
|
-
If the
|
271
|
+
If you find you have many hashes or the content of the script tags change frequently, you can apply these hashes in a more intelligent way. This method expects config/script_hashes.yml to contain a map of templates => [hashes]. When the individual templates, layouts, or partials are rendered the hash values for the script tags in those templates will be automatically added to the header. *Currently, only erb layouts are supported.* This requires the use of middleware:
|
265
272
|
|
266
273
|
```ruby
|
267
|
-
|
274
|
+
# config.ru
|
275
|
+
require 'secure_headers/headers/content_security_policy/script_hash_middleware'
|
276
|
+
use ::SecureHeaders::ContentSecurityPolicy::ScriptHashMiddleware
|
268
277
|
```
|
278
|
+
|
279
|
+
```ruby
|
280
|
+
config.csp = {
|
281
|
+
:default_src => "https:",
|
282
|
+
:script_src => 'self',
|
283
|
+
:script_hash_middleware => true
|
284
|
+
}
|
285
|
+
```
|
286
|
+
|
287
|
+
Hashes are stored in a yaml file with a mapping of Filename => [list of hashes] in config/script_hashes.yml. You can automatically populate this file by running the following rake task:
|
288
|
+
|
289
|
+
```$ bundle exec rake secure_headers:generate_hashes```
|
290
|
+
|
291
|
+
Which will generate something like:
|
292
|
+
|
293
|
+
```yaml
|
294
|
+
# config/script_hashes.yml
|
295
|
+
app/views/layouts/application.html.erb:
|
296
|
+
- sha256-l8OLjZqYRnKilpdE0VosRMvhdYArjXT4NZaK2p7QVvs=
|
297
|
+
app/templates/articles/edit.html.erb:
|
298
|
+
- sha256-+7mij1/uCwtCQRWrof2NmOln5qX+5WdVwTLMpi8nuoA=
|
299
|
+
- sha256-Ny4TRIhhFpnYnSeKC274P6bfAz4TOkezLabavIAU4dA=
|
300
|
+
- sha256-I5e58Gqbu4WpO9dck18QxO7aYOHKrELIi70it4jIPi0=
|
301
|
+
- sha256-Po4LMynwnAJHxiTp3DQaQ3YDBj3paN/xrDoKl4OyxY4=
|
302
|
+
```
|
303
|
+
|
304
|
+
In this example, if we visit /articles/edit/[id], the above hashes will automatically be added to the CSP header's
|
305
|
+
script-src value!
|
306
|
+
|
307
|
+
You can use plain "script" tags or you can use a built-in helper:
|
308
|
+
|
309
|
+
```erb
|
310
|
+
<%= hashed_javascript_tag do %>
|
311
|
+
console.log("hashed automatically!")
|
312
|
+
<% end %>
|
313
|
+
```
|
314
|
+
|
315
|
+
By using the helper, hash values will be computed dynamically in development/test environments. If a dynamically computed hash value does not match what is expected to be found in config/script_hashes.yml a warning message will be printed to the console. If you want to raise exceptions instead, use:
|
316
|
+
|
317
|
+
```erb
|
318
|
+
<%= hashed_javascript_tag(raise_error_on_unrecognized_hash = true) do %>
|
319
|
+
console.log("will raise an exception if not in script_hashes.yml!")
|
320
|
+
<% end %>
|
321
|
+
```
|
322
|
+
|
269
323
|
### Using with Sinatra
|
270
324
|
|
271
325
|
Here's an example using SecureHeaders for Sinatra applications:
|
@@ -282,10 +336,10 @@ require 'secure_headers'
|
|
282
336
|
config.x_content_type_options = "nosniff"
|
283
337
|
config.x_xss_protection = {:value => 1, :mode => false}
|
284
338
|
config.csp = {
|
285
|
-
:default_src => "https
|
339
|
+
:default_src => "https: inline eval",
|
286
340
|
:report_uri => '//example.com/uri-directive',
|
287
|
-
:img_src => "https
|
288
|
-
:frame_src => "https
|
341
|
+
:img_src => "https: data:",
|
342
|
+
:frame_src => "https: http:.twimg.com http://itunes.apple.com"
|
289
343
|
}
|
290
344
|
end
|
291
345
|
|
@@ -337,10 +391,10 @@ def before_load
|
|
337
391
|
config.x_content_type_options = "nosniff"
|
338
392
|
config.x_xss_protection = {:value => '1', :mode => false}
|
339
393
|
config.csp = {
|
340
|
-
:default_src => "https
|
394
|
+
:default_src => "https: inline eval",
|
341
395
|
:report_uri => '//example.com/uri-directive',
|
342
|
-
:img_src => "https
|
343
|
-
:frame_src => "https
|
396
|
+
:img_src => "https: data:",
|
397
|
+
:frame_src => "https: http:.twimg.com http://itunes.apple.com"
|
344
398
|
}
|
345
399
|
end
|
346
400
|
end
|
data/Rakefile
CHANGED
@@ -49,119 +49,3 @@ RDoc::Task.new(:rdoc) do |rdoc|
|
|
49
49
|
rdoc.options << '--line-numbers'
|
50
50
|
rdoc.rdoc_files.include('lib/**/*.rb')
|
51
51
|
end
|
52
|
-
|
53
|
-
UPDATE_URI = 'https://mxr.mozilla.org/mozilla/source/security/nss/lib/ckfw/builtins/certdata.txt?raw=1'
|
54
|
-
CA_FILE = File.expand_path(File.join('..', 'config', 'curl-ca-bundle.crt'), __FILE__)
|
55
|
-
task :fetch_ca_bundle do
|
56
|
-
begin
|
57
|
-
FileUtils.cp CA_FILE, CA_FILE + ".bak"
|
58
|
-
uri = URI.parse(UPDATE_URI)
|
59
|
-
http = Net::HTTP.new(uri.host, uri.port)
|
60
|
-
http.use_ssl = true
|
61
|
-
http.ca_file = CA_FILE
|
62
|
-
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
|
63
|
-
request = Net::HTTP::Get.new(uri.request_uri)
|
64
|
-
|
65
|
-
ca_file = StringIO.new(http.request(request).body)
|
66
|
-
File.open(CA_FILE, 'w') do |f|
|
67
|
-
f.puts mozilla_license
|
68
|
-
end
|
69
|
-
|
70
|
-
while line = ca_file.gets
|
71
|
-
next if line =~ /^#/
|
72
|
-
next if line =~ /^\s*$/
|
73
|
-
line.chomp!
|
74
|
-
|
75
|
-
if line =~ /CKA_LABEL/
|
76
|
-
label,type,cert_name = line.split(' ',3)
|
77
|
-
cert_name.sub!(/^"/, "")
|
78
|
-
cert_name.sub!(/"$/, "")
|
79
|
-
next
|
80
|
-
end
|
81
|
-
if line =~ /CKA_VALUE MULTILINE_OCTAL/
|
82
|
-
puts "reading cert for #{cert_name}"
|
83
|
-
data=''
|
84
|
-
while line = ca_file.gets
|
85
|
-
break if line =~ /^END/
|
86
|
-
line.chomp!
|
87
|
-
line.gsub(/\\([0-3][0-7][0-7])/) { data += $1.oct.chr }
|
88
|
-
end
|
89
|
-
|
90
|
-
open(CA_FILE, "a") do |fp|
|
91
|
-
puts "Appending"
|
92
|
-
fp.puts cert_name
|
93
|
-
fp.puts "================"
|
94
|
-
fp.puts "-----BEGIN CERTIFICATE-----"
|
95
|
-
fp.puts [data].pack("m*")
|
96
|
-
fp.puts "-----END CERTIFICATE-----"
|
97
|
-
fp.puts
|
98
|
-
end
|
99
|
-
puts "Parsing: " + cert_name
|
100
|
-
end
|
101
|
-
end
|
102
|
-
|
103
|
-
FileUtils.rm CA_FILE + ".bak"
|
104
|
-
rescue => e
|
105
|
-
puts "ERRROR #{e}"
|
106
|
-
puts e.backtrace
|
107
|
-
FileUtils.mv CA_FILE + '.bak', CA_FILE
|
108
|
-
end
|
109
|
-
end
|
110
|
-
|
111
|
-
|
112
|
-
def mozilla_license
|
113
|
-
<<-EOM
|
114
|
-
## generated using a modified version of http://curl.haxx.se/mail/lib-2004-07/att-0134/parse-certs.sh
|
115
|
-
##
|
116
|
-
## lib/ca-bundle.crt -- Bundle of CA Root Certificates
|
117
|
-
##
|
118
|
-
## Certificate data from Mozilla as of: Tue Mar 27 20:21:58 2012
|
119
|
-
##
|
120
|
-
## This is a bundle of X.509 certificates of public Certificate Authorities
|
121
|
-
## (CA). These were automatically extracted from Mozilla's root certificates
|
122
|
-
## file (certdata.txt). This file can be found in the mozilla source tree:
|
123
|
-
## http://mxr.mozilla.org/mozilla/source/security/nss/lib/ckfw/builtins/certdata.txt?raw=1
|
124
|
-
##
|
125
|
-
## It contains the certificates in PEM format and therefore
|
126
|
-
## can be directly used with curl / libcurl / php_curl, or with
|
127
|
-
## an Apache+mod_ssl webserver for SSL client authentication.
|
128
|
-
## Just configure this file as the SSLCACertificateFile.
|
129
|
-
##
|
130
|
-
|
131
|
-
# ***** BEGIN LICENSE BLOCK *****
|
132
|
-
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
133
|
-
#
|
134
|
-
# The contents of this file are subject to the Mozilla Public License Version
|
135
|
-
# 1.1 (the "License"); you may not use this file except in compliance with
|
136
|
-
# the License. You may obtain a copy of the License at
|
137
|
-
# http://www.mozilla.org/MPL/
|
138
|
-
#
|
139
|
-
# Software distributed under the License is distributed on an "AS IS" basis,
|
140
|
-
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
141
|
-
# for the specific language governing rights and limitations under the
|
142
|
-
# License.
|
143
|
-
#
|
144
|
-
# The Original Code is the Netscape security libraries.
|
145
|
-
#
|
146
|
-
# The Initial Developer of the Original Code is
|
147
|
-
# Netscape Communications Corporation.
|
148
|
-
# Portions created by the Initial Developer are Copyright (C) 1994-2000
|
149
|
-
# the Initial Developer. All Rights Reserved.
|
150
|
-
#
|
151
|
-
# Contributor(s):
|
152
|
-
#
|
153
|
-
# Alternatively, the contents of this file may be used under the terms of
|
154
|
-
# either the GNU General Public License Version 2 or later (the "GPL"), or
|
155
|
-
# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
156
|
-
# in which case the provisions of the GPL or the LGPL are applicable instead
|
157
|
-
# of those above. If you wish to allow use of your version of this file only
|
158
|
-
# under the terms of either the GPL or the LGPL, and not to allow others to
|
159
|
-
# use your version of this file under the terms of the MPL, indicate your
|
160
|
-
# decision by deleting the provisions above and replace them with the notice
|
161
|
-
# and other provisions required by the GPL or the LGPL. If you do not delete
|
162
|
-
# the provisions above, a recipient may use your version of this file under
|
163
|
-
# the terms of any one of the MPL, the GPL or the LGPL.
|
164
|
-
#
|
165
|
-
# ***** END LICENSE BLOCK *****
|
166
|
-
EOM
|
167
|
-
end
|
@@ -1 +1,2 @@
|
|
1
|
-
index
|
1
|
+
index
|
2
|
+
<script>console.log("oh what")</script>
|
@@ -6,9 +6,9 @@
|
|
6
6
|
csp = {
|
7
7
|
:default_src => "self",
|
8
8
|
:script_src => "self nonce",
|
9
|
-
:disable_chrome_extension => true,
|
10
9
|
:disable_fill_missing => true,
|
11
10
|
:report_uri => 'somewhere',
|
11
|
+
:script_hash_middleware => true,
|
12
12
|
:enforce => false # false means warnings only
|
13
13
|
}
|
14
14
|
|
@@ -1,45 +1,77 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
+
require 'secure_headers/headers/content_security_policy/script_hash_middleware'
|
4
|
+
|
3
5
|
describe OtherThingsController, :type => :controller do
|
6
|
+
include Rack::Test::Methods
|
7
|
+
|
8
|
+
def app
|
9
|
+
OtherThingsController.action(:index)
|
10
|
+
end
|
11
|
+
|
12
|
+
def request(opts = {})
|
13
|
+
options = opts.merge(
|
14
|
+
{
|
15
|
+
'HTTPS' => 'on',
|
16
|
+
'HTTP_USER_AGENT' => "Mozilla/5.0 (compatible; MSIE 10.6; Windows NT 6.1; Trident/5.0; InfoPath.2; SLCC1; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729; .NET CLR 2.0.50727) 3gpp-gba UNTRUSTED/1.0"
|
17
|
+
}
|
18
|
+
)
|
19
|
+
|
20
|
+
|
21
|
+
Rack::MockRequest.env_for('/', options)
|
22
|
+
end
|
23
|
+
|
24
|
+
|
4
25
|
describe "headers" do
|
26
|
+
before(:each) do
|
27
|
+
_, @env = app.call(request)
|
28
|
+
end
|
29
|
+
|
5
30
|
it "sets the X-XSS-Protection header" do
|
6
|
-
get
|
7
|
-
expect(
|
31
|
+
get '/'
|
32
|
+
expect(@env['X-XSS-Protection']).to eq('1; mode=block')
|
8
33
|
end
|
9
34
|
|
10
35
|
it "sets the X-Frame-Options header" do
|
11
|
-
get
|
12
|
-
expect(
|
36
|
+
get '/'
|
37
|
+
expect(@env['X-Frame-Options']).to eq('SAMEORIGIN')
|
13
38
|
end
|
14
39
|
|
15
40
|
it "sets the CSP header with a local reference to a nonce" do
|
16
|
-
|
17
|
-
|
18
|
-
expect(
|
19
|
-
|
41
|
+
middleware = ::SecureHeaders::ContentSecurityPolicy::ScriptHashMiddleware.new(app)
|
42
|
+
_, env = middleware.call(request(@env))
|
43
|
+
expect(env['Content-Security-Policy-Report-Only']).to match(/script-src[^;]*'nonce-[a-zA-Z0-9\+\/=]{44}'/)
|
44
|
+
end
|
45
|
+
|
46
|
+
it "sets the required hashes to whitelist inline script" do
|
47
|
+
middleware = ::SecureHeaders::ContentSecurityPolicy::ScriptHashMiddleware.new(app)
|
48
|
+
_, env = middleware.call(request(@env))
|
49
|
+
hashes = ['sha256-VjDxT7saxd2FgaUQQTWw/jsTnvonaoCP/ACWDBTpyhU=', 'sha256-ZXAcP8a0y1pPMTJW8pUr43c+XBkgYQBwHOPvXk9mq5A=']
|
50
|
+
hashes.each do |hash|
|
51
|
+
expect(env['Content-Security-Policy-Report-Only']).to include(hash)
|
52
|
+
end
|
20
53
|
end
|
21
54
|
|
22
55
|
it "sets the Strict-Transport-Security header" do
|
23
|
-
|
24
|
-
|
25
|
-
expect(response.headers['Strict-Transport-Security']).to eq("max-age=315576000")
|
56
|
+
get '/'
|
57
|
+
expect(@env['Strict-Transport-Security']).to eq("max-age=315576000")
|
26
58
|
end
|
27
59
|
|
28
60
|
it "sets the X-Download-Options header" do
|
29
|
-
get
|
30
|
-
expect(
|
61
|
+
get '/'
|
62
|
+
expect(@env['X-Download-Options']).to eq('noopen')
|
31
63
|
end
|
32
64
|
|
33
65
|
it "sets the X-Content-Type-Options header" do
|
34
|
-
get
|
35
|
-
expect(
|
66
|
+
get '/'
|
67
|
+
expect(@env['X-Content-Type-Options']).to eq("nosniff")
|
36
68
|
end
|
37
69
|
|
38
70
|
context "using IE" do
|
39
71
|
it "sets the X-Content-Type-Options header" do
|
40
|
-
|
41
|
-
get
|
42
|
-
expect(
|
72
|
+
@env['HTTP_USER_AGENT'] = "Mozilla/5.0 (compatible; MSIE 10.6; Windows NT 6.1; Trident/5.0; InfoPath.2; SLCC1; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729; .NET CLR 2.0.50727) 3gpp-gba UNTRUSTED/1.0"
|
73
|
+
get '/'
|
74
|
+
expect(@env['X-Content-Type-Options']).to eq("nosniff")
|
43
75
|
end
|
44
76
|
end
|
45
77
|
end
|
@@ -16,7 +16,7 @@ describe ThingsController, :type => :controller do
|
|
16
16
|
expect(response.headers['X-Frame-Options']).to eq('SAMEORIGIN')
|
17
17
|
end
|
18
18
|
|
19
|
-
it "
|
19
|
+
it "does not set CSP header" do
|
20
20
|
get :index
|
21
21
|
expect(response.headers['Content-Security-Policy-Report-Only']).to eq(nil)
|
22
22
|
end
|
@@ -1,6 +1,5 @@
|
|
1
1
|
class OtherThingsController < ApplicationController
|
2
|
-
ensure_security_headers :csp => {:default_src => 'self', :
|
3
|
-
:disable_fill_missing => true}
|
2
|
+
ensure_security_headers :csp => {:default_src => 'self', :disable_fill_missing => true}
|
4
3
|
def index
|
5
4
|
|
6
5
|
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module SecureHeaders
|
2
|
+
class ContentSecurityPolicy
|
3
|
+
class ScriptHashMiddleware
|
4
|
+
def initialize(app)
|
5
|
+
@app = app
|
6
|
+
end
|
7
|
+
|
8
|
+
def call(env)
|
9
|
+
status, headers, response = @app.call(env)
|
10
|
+
metadata = env[ContentSecurityPolicy::ENV_KEY]
|
11
|
+
if !metadata.nil?
|
12
|
+
config, options = metadata.values_at(:config, :options)
|
13
|
+
config.merge!(:script_hashes => env[HASHES_ENV_KEY])
|
14
|
+
csp_header = ContentSecurityPolicy.new(config, options)
|
15
|
+
headers[csp_header.name] = csp_header.value
|
16
|
+
end
|
17
|
+
|
18
|
+
[status, headers, response]
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|