secure_headers 3.5.1 → 3.6.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d196b33a65e52d7a5e51d9aab6bece9d5931f3ec
4
- data.tar.gz: ef4bff640a44b916d0d4abb568e32c55dfc5d184
3
+ metadata.gz: 7585a939ce1ded2d2226ba1d1503a5f0dca3eb49
4
+ data.tar.gz: 3762611d81eb31f24cdfd04a609d7aaed5a62182
5
5
  SHA512:
6
- metadata.gz: 4b90556098db0af1a3469bee1c456291a0a68b44559e480faf08568a5e4656b0c53f0b5f5fe926f657c8884b8057a6ec545711998a359aeaef88ec10bc099e7a
7
- data.tar.gz: 226f55c1d0ab7c8b13d5ef13c8baaedf1162c5afe9ab0b88e04b6a092b4fb01fbdbcd6af840992e39e9011d5686b53c2744e0ee6b39774047328c171edad29af
6
+ metadata.gz: 4ba111dea43dd24b25567ff18e98fb1de8ad5f5a04566a9390e9a20e9c405571038960bd3dca417d225f58deea82214e27d8381448c887607db2883cd71b6dda
7
+ data.tar.gz: 7dd6cf414c1e3ed0814b5095730cd98ee77259c3560dc7a9371754dbbbff837f44a7cc93d664f85d7317fb6f633f0b2a5d8145f7d7fac5833a5dfc83fa0b4a72
@@ -1,3 +1,7 @@
1
+ ## 3.6.0
2
+
3
+ Add support for the clear-site-data header
4
+
1
5
  ## 3.5.1
2
6
 
3
7
  * Fix bug that can occur when useragent library version is older, resulting in a nil version sometimes.
data/README.md CHANGED
@@ -18,11 +18,35 @@ The gem will automatically apply several headers that are related to security.
18
18
  - X-Permitted-Cross-Domain-Policies - [Restrict Adobe Flash Player's access to data](https://www.adobe.com/devnet/adobe-media-server/articles/cross-domain-xml-for-streaming.html)
19
19
  - Referrer-Policy - [Referrer Policy draft](https://w3c.github.io/webappsec-referrer-policy/)
20
20
  - Public Key Pinning - Pin certificate fingerprints in the browser to prevent man-in-the-middle attacks due to compromised Certificate Authorities. [Public Key Pinning Specification](https://tools.ietf.org/html/rfc7469)
21
+ - Clear-Site-Data - Clearing browser data for origin. [Clear-Site-Data specification](https://www.w3.org/TR/clear-site-data/).
21
22
 
22
23
  It can also mark all http cookies with the Secure, HttpOnly and SameSite attributes (when configured to do so).
23
24
 
24
25
  `secure_headers` is a library with a global config, per request overrides, and rack middleware that enables you customize your application settings.
25
26
 
27
+ ## Documentation
28
+
29
+ - [Named overrides and appends](docs/named_overrides_and_appends.md)
30
+ - [Per action configuration](docs/per_action_configuration.md)
31
+ - [Cookies](docs/cookies.md)
32
+ - [HPKP](docs/HPKP.md)
33
+ - [Hashes](docs/hashes.md)
34
+ - [Sinatra Config](docs/sinatra.md)
35
+
36
+ ## Getting Started
37
+
38
+ ### Rails 3+
39
+
40
+ For Rails 3+ applications, `secure_headers` has a `railtie` that should automatically include the middleware. If for some reason the middleware is not being included follow the instructions for Rails 2.
41
+
42
+ ### Rails 2
43
+
44
+ For Rails 2 or non-rails applications, an explicit statement is required to use the middleware component.
45
+
46
+ ```ruby
47
+ use SecureHeaders::Middleware
48
+ ```
49
+
26
50
  ## Configuration
27
51
 
28
52
  If you do not supply a `default` configuration, exceptions will be raised. If you would like to use a default configuration (which is fairly locked down), just call `SecureHeaders::Configuration.default` without any arguments or block.
@@ -45,6 +69,12 @@ SecureHeaders::Configuration.default do |config|
45
69
  config.x_download_options = "noopen"
46
70
  config.x_permitted_cross_domain_policies = "none"
47
71
  config.referrer_policy = "origin-when-cross-origin"
72
+ config.clear_site_data = [
73
+ "cache",
74
+ "cookies",
75
+ "storage",
76
+ "executionContexts"
77
+ ]
48
78
  config.csp = {
49
79
  # "meta" values. these will shaped the header, but the values are not included in the header.
50
80
  report_only: true, # default: false [DEPRECATED from 3.5.0: instead, configure csp_report_only]
@@ -86,17 +116,9 @@ SecureHeaders::Configuration.default do |config|
86
116
  end
87
117
  ```
88
118
 
89
- ### rails 2
90
-
91
- For rails 3+ applications, `secure_headers` has a `railtie` that should automatically include the middleware. For rails 2 or non-rails applications, an explicit statement is required to use the middleware component.
92
-
93
- ```ruby
94
- use SecureHeaders::Middleware
95
- ```
96
-
97
119
  ## Default values
98
120
 
99
- All headers except for PublicKeyPins have a default value. See the [corresponding classes for their defaults](https://github.com/twitter/secureheaders/tree/master/lib/secure_headers/headers). The default set of headers is:
121
+ All headers except for PublicKeyPins and ClearSiteData have a default value. The default set of headers is:
100
122
 
101
123
  ```
102
124
  Content-Security-Policy: default-src 'self' https:; font-src 'self' https: data:; img-src 'self' https: data:; object-src 'none'; script-src https:; style-src 'self' https: 'unsafe-inline'
@@ -121,379 +143,6 @@ Configuration.default do |config|
121
143
  end
122
144
  ```
123
145
 
124
- ## Named Appends
125
-
126
- Named Appends are blocks of code that can be reused and composed during requests. e.g. If a certain partial is rendered conditionally, and the csp needs to be adjusted for that partial, you can create a named append for that situation. The value returned by the block will be passed into `append_content_security_policy_directives`. The current request object is passed as an argument to the block for even more flexibility.
127
-
128
- ```ruby
129
- def show
130
- if include_widget?
131
- @widget = widget.render
132
- use_content_security_policy_named_append(:widget_partial)
133
- end
134
- end
135
-
136
-
137
- SecureHeaders::Configuration.named_append(:widget_partial) do |request|
138
- SecureHeaders.override_x_frame_options(request, "DENY")
139
- if request.controller_instance.current_user.in_test_bucket?
140
- { child_src: %w(beta.thirdpartyhost.com) }
141
- else
142
- { child_src: %w(thirdpartyhost.com) }
143
- end
144
- end
145
- ```
146
-
147
- You can use as many named appends as you would like per request, but be careful because order of inclusion matters. Consider the following:
148
-
149
- ```ruby
150
- SecureHeader::Configuration.default do |config|
151
- config.csp = { default_src: %w('self')}
152
- end
153
-
154
- SecureHeaders::Configuration.named_append(:A) do |request|
155
- { default_src: %w(myhost.com) }
156
- end
157
-
158
- SecureHeaders::Configuration.named_append(:B) do |request|
159
- { script_src: %w('unsafe-eval') }
160
- end
161
- ```
162
-
163
- The following code will produce different policies due to the way policies are normalized (e.g. providing a previously undefined directive that inherits from `default-src`, removing host source values when `*` is provided. Removing `'none'` when additional values are present, etc.):
164
-
165
- ```ruby
166
- def index
167
- use_content_security_policy_named_append(:A)
168
- use_content_security_policy_named_append(:B)
169
- # produces default-src 'self' myhost.com; script-src 'self' myhost.com 'unsafe-eval';
170
- end
171
-
172
- def show
173
- use_content_security_policy_named_append(:B)
174
- use_content_security_policy_named_append(:A)
175
- # produces default-src 'self' myhost.com; script-src 'self' 'unsafe-eval';
176
- end
177
- ```
178
-
179
-
180
- ## Named overrides
181
-
182
- Named overrides serve two purposes:
183
-
184
- * To be able to refer to a configuration by simple name.
185
- * By precomputing the headers for a named configuration, the headers generated once and reused over every request.
186
-
187
- To use a named override, drop a `SecureHeaders::Configuration.override` block **outside** of method definitions and then declare which named override you'd like to use. You can even override an override.
188
-
189
- ```ruby
190
- class ApplicationController < ActionController::Base
191
- SecureHeaders::Configuration.default do |config|
192
- config.csp = {
193
- default_src: %w('self'),
194
- script_src: %w(example.org)
195
- }
196
- end
197
-
198
- # override default configuration
199
- SecureHeaders::Configuration.override(:script_from_otherdomain_com) do |config|
200
- config.csp[:script_src] << "otherdomain.com"
201
- end
202
-
203
- # overrides the :script_from_otherdomain_com configuration
204
- SecureHeaders::Configuration.override(:another_config, :script_from_otherdomain_com) do |config|
205
- config.csp[:script_src] << "evenanotherdomain.com"
206
- end
207
- end
208
-
209
- class MyController < ApplicationController
210
- def index
211
- # Produces default-src 'self'; script-src example.org otherdomain.com
212
- use_secure_headers_override(:script_from_otherdomain_com)
213
- end
214
-
215
- def show
216
- # Produces default-src 'self'; script-src example.org otherdomain.org evenanotherdomain.com
217
- use_secure_headers_override(:another_config)
218
- end
219
- end
220
- ```
221
-
222
- By default, a no-op configuration is provided. No headers will be set when this default override is used.
223
-
224
- ```ruby
225
- class MyController < ApplicationController
226
- def index
227
- SecureHeaders.opt_out_of_all_protection(request)
228
- end
229
- end
230
- ```
231
-
232
- ## Per-action configuration
233
-
234
- You can override the settings for a given action by producing a temporary override. Be aware that because of the dynamic nature of the value, the header values will be computed per request.
235
-
236
- ```ruby
237
- # Given a config of:
238
- ::SecureHeaders::Configuration.default do |config|
239
- config.csp = {
240
- default_src: %w('self'),
241
- script_src: %w('self')
242
- }
243
- end
244
-
245
- class MyController < ApplicationController
246
- def index
247
- # Append value to the source list, override 'none' values
248
- # Produces: default-src 'self'; script-src 'self' s3.amazonaws.com; object-src 'self' www.youtube.com
249
- append_content_security_policy_directives(script_src: %w(s3.amazonaws.com), object_src: %w('self' www.youtube.com))
250
-
251
- # Overrides the previously set source list, override 'none' values
252
- # Produces: default-src 'self'; script-src s3.amazonaws.com; object-src 'self'
253
- override_content_security_policy_directives(script_src: %w(s3.amazonaws.com), object_src: %w('self'))
254
-
255
- # Global settings default to "sameorigin"
256
- override_x_frame_options("DENY")
257
- end
258
- ```
259
-
260
- The following methods are available as controller instance methods. They are also available as class methods, but require you to pass in the `request` object.
261
- * `append_content_security_policy_directives(hash)`: appends each value to the corresponding CSP app-wide configuration.
262
- * `override_content_security_policy_directives(hash)`: merges the hash into the app-wide configuration, overwriting any previous config
263
- * `override_x_frame_options(value)`: sets the `X-Frame-Options header` to `value`
264
-
265
- ## Appending / overriding Content Security Policy
266
-
267
- When manipulating content security policy, there are a few things to consider. The default header value is `default-src https:` which corresponds to a default configuration of `{ default_src: %w(https:)}`.
268
-
269
- #### Append to the policy with a directive other than `default_src`
270
-
271
- The value of `default_src` is joined with the addition if the it is a [fetch directive](https://w3c.github.io/webappsec-csp/#directives-fetch). Note the `https:` is carried over from the `default-src` config. If you do not want this, use `override_content_security_policy_directives` instead. To illustrate:
272
-
273
- ```ruby
274
- ::SecureHeaders::Configuration.default do |config|
275
- config.csp = {
276
- default_src: %w('self')
277
- }
278
- end
279
- ```
280
-
281
- Code | Result
282
- ------------- | -------------
283
- `append_content_security_policy_directives(script_src: %w(mycdn.com))` | `default-src 'self'; script-src 'self' mycdn.com`
284
- `override_content_security_policy_directives(script_src: %w(mycdn.com))` | `default-src 'self'; script-src mycdn.com`
285
-
286
- #### Nonce
287
-
288
- You can use a view helper to automatically add nonces to script tags:
289
-
290
- ```erb
291
- <%= nonced_javascript_tag do %>
292
- console.log("nonced!");
293
- <% end %>
294
-
295
- <%= nonced_style_tag do %>
296
- body {
297
- background-color: black;
298
- }
299
- <% end %>
300
- ```
301
-
302
- becomes:
303
-
304
- ```html
305
- <script nonce="/jRAxuLJsDXAxqhNBB7gg7h55KETtDQBXe4ZL+xIXwI=">
306
- console.log("nonced!")
307
- </script>
308
- <style nonce="/jRAxuLJsDXAxqhNBB7gg7h55KETtDQBXe4ZL+xIXwI=">
309
- body {
310
- background-color: black;
311
- }
312
- </style>
313
- ```
314
-
315
- ```
316
-
317
- Content-Security-Policy: ...
318
- script-src 'nonce-/jRAxuLJsDXAxqhNBB7gg7h55KETtDQBXe4ZL+xIXwI=' ...;
319
- style-src 'nonce-/jRAxuLJsDXAxqhNBB7gg7h55KETtDQBXe4ZL+xIXwI=' ...;
320
- ```
321
-
322
- `script`/`style-nonce` can be used to whitelist inline content. To do this, call the `content_security_policy_script_nonce` or `content_security_policy_style_nonce` then set the nonce attributes on the various tags.
323
-
324
- ```erb
325
- <script nonce="<%= content_security_policy_script_nonce %>">
326
- console.log("whitelisted, will execute")
327
- </script>
328
-
329
- <script nonce="lol">
330
- console.log("won't execute, not whitelisted")
331
- </script>
332
-
333
- <script>
334
- console.log("won't execute, not whitelisted")
335
- </script>
336
- ```
337
-
338
- #### Hash
339
-
340
- `script`/`style-src` hashes can be used to whitelist inline content that is static. This has the benefit of allowing inline content without opening up the possibility of dynamic javascript like you would with a `nonce`.
341
-
342
- You can add hash sources directly to your policy :
343
-
344
- ```ruby
345
- ::SecureHeaders::Configuration.default do |config|
346
- config.csp = {
347
- default_src: %w('self')
348
-
349
- # this is a made up value but browsers will show the expected hash in the console.
350
- script_src: %w(sha256-123456)
351
- }
352
- end
353
- ```
354
-
355
- You can also use the automated inline script detection/collection/computation of hash source values in your app.
356
-
357
- ```bash
358
- rake secure_headers:generate_hashes
359
- ```
360
-
361
- This will generate a file (`config/config/secure_headers_generated_hashes.yml` by default, you can override by setting `ENV["secure_headers_generated_hashes_file"]`) containing a mapping of file names with the array of hash values found on that page. When ActionView renders a given file, we check if there are any known hashes for that given file. If so, they are added as values to the header.
362
-
363
- ```yaml
364
- ---
365
- scripts:
366
- app/views/asdfs/index.html.erb:
367
- - "'sha256-yktKiAsZWmc8WpOyhnmhQoDf9G2dAZvuBBC+V0LGQhg='"
368
- styles:
369
- app/views/asdfs/index.html.erb:
370
- - "'sha256-SLp6LO3rrKDJwsG9uJUxZapb4Wp2Zhj6Bu3l+d9rnAY='"
371
- - "'sha256-HSGHqlRoKmHAGTAJ2Rq0piXX4CnEbOl1ArNd6ejp2TE='"
372
- ```
373
-
374
- ##### Helpers
375
-
376
- **This will not compute dynamic hashes** by design. The output of both helpers will be a plain `script`/`style` tag without modification and the known hashes for a given file will be added to `script-src`/`style-src` when `hashed_javascript_tag` and `hashed_style_tag` are used. You can use `raise_error_on_unrecognized_hash = true` to be extra paranoid that you have precomputed hash values for all of your inline content. By default, this will raise an error in non-production environments.
377
-
378
- ```erb
379
- <%= hashed_style_tag do %>
380
- body {
381
- background-color: black;
382
- }
383
- <% end %>
384
-
385
- <%= hashed_style_tag do %>
386
- body {
387
- font-size: 30px;
388
- font-color: green;
389
- }
390
- <% end %>
391
-
392
- <%= hashed_javascript_tag do %>
393
- console.log(1)
394
- <% end %>
395
- ```
396
-
397
- ```
398
- Content-Security-Policy: ...
399
- script-src 'sha256-yktKiAsZWmc8WpOyhnmhQoDf9G2dAZvuBBC+V0LGQhg=' ... ;
400
- style-src 'sha256-SLp6LO3rrKDJwsG9uJUxZapb4Wp2Zhj6Bu3l+d9rnAY=' 'sha256-HSGHqlRoKmHAGTAJ2Rq0piXX4CnEbOl1ArNd6ejp2TE=' ...;
401
- ```
402
-
403
- ### Public Key Pins
404
-
405
- Be aware that pinning error reporting is governed by the same rules as everything else. If you have a pinning failure that tries to report back to the same origin, by definition this will not work.
406
-
407
- ```ruby
408
- config.hpkp = {
409
- max_age: 60.days.to_i, # max_age is a required parameter
410
- include_subdomains: true, # whether or not to apply pins to subdomains
411
- # Per the spec, SHA256 hashes are the only currently supported format.
412
- pins: [
413
- {sha256: 'b5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b878ae4944c'},
414
- {sha256: '73a2c64f9545172c1195efb6616ca5f7afd1df6f245407cafb90de3998a1c97f'}
415
- ],
416
- report_only: true, # defaults to false (report-only mode)
417
- report_uri: 'https://report-uri.io/example-hpkp'
418
- }
419
- ```
420
-
421
- ### Cookies
422
-
423
- SecureHeaders supports `Secure`, `HttpOnly` and [`SameSite`](https://tools.ietf.org/html/draft-west-first-party-cookies-07) cookies. These can be defined in the form of a boolean, or as a Hash for more refined configuration.
424
-
425
- __Note__: Regardless of the configuration specified, Secure cookies are only enabled for HTTPS requests.
426
-
427
- #### Boolean-based configuration
428
-
429
- Boolean-based configuration is intended to globally enable or disable a specific cookie attribute.
430
-
431
- ```ruby
432
- config.cookies = {
433
- secure: true, # mark all cookies as Secure
434
- httponly: false, # do not mark any cookies as HttpOnly
435
- }
436
- ```
437
-
438
- #### Hash-based configuration
439
-
440
- Hash-based configuration allows for fine-grained control.
441
-
442
- ```ruby
443
- config.cookies = {
444
- secure: { except: ['_guest'] }, # mark all but the `_guest` cookie as Secure
445
- httponly: { only: ['_rails_session'] }, # only mark the `_rails_session` cookie as HttpOnly
446
- }
447
- ```
448
-
449
- #### SameSite cookie configuration
450
-
451
- SameSite cookies permit either `Strict` or `Lax` enforcement mode options.
452
-
453
- ```ruby
454
- config.cookies = {
455
- samesite: {
456
- strict: true # mark all cookies as SameSite=Strict
457
- }
458
- }
459
- ```
460
-
461
- `Strict` and `Lax` enforcement modes can also be specified using a Hash.
462
-
463
- ```ruby
464
- config.cookies = {
465
- samesite: {
466
- strict: { only: ['_rails_session'] },
467
- lax: { only: ['_guest'] }
468
- }
469
- }
470
- ```
471
-
472
- ### Using with Sinatra
473
-
474
- Here's an example using SecureHeaders for Sinatra applications:
475
-
476
- ```ruby
477
- require 'rubygems'
478
- require 'sinatra'
479
- require 'haml'
480
- require 'secure_headers'
481
-
482
- use SecureHeaders::Middleware
483
-
484
- SecureHeaders::Configuration.default do |config|
485
- ...
486
- end
487
-
488
- class Donkey < Sinatra::Application
489
- set :root, APP_ROOT
490
-
491
- get '/' do
492
- SecureHeaders.override_x_frame_options(request, SecureHeaders::OPT_OUT)
493
- haml :index
494
- end
495
- end
496
- ```
497
146
 
498
147
  ## Similar libraries
499
148
 
@@ -0,0 +1,17 @@
1
+ ## HTTP Public Key Pins
2
+
3
+ Be aware that pinning error reporting is governed by the same rules as everything else. If you have a pinning failure that tries to report back to the same origin, by definition this will not work.
4
+
5
+ ```ruby
6
+ config.hpkp = {
7
+ max_age: 60.days.to_i, # max_age is a required parameter
8
+ include_subdomains: true, # whether or not to apply pins to subdomains
9
+ # Per the spec, SHA256 hashes are the only currently supported format.
10
+ pins: [
11
+ {sha256: 'b5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b878ae4944c'},
12
+ {sha256: '73a2c64f9545172c1195efb6616ca5f7afd1df6f245407cafb90de3998a1c97f'}
13
+ ],
14
+ report_only: true, # defaults to false (report-only mode)
15
+ report_uri: 'https://report-uri.io/example-hpkp'
16
+ }
17
+ ```
@@ -0,0 +1,50 @@
1
+ ## Cookies
2
+
3
+ SecureHeaders supports `Secure`, `HttpOnly` and [`SameSite`](https://tools.ietf.org/html/draft-west-first-party-cookies-07) cookies. These can be defined in the form of a boolean, or as a Hash for more refined configuration.
4
+
5
+ __Note__: Regardless of the configuration specified, Secure cookies are only enabled for HTTPS requests.
6
+
7
+ #### Boolean-based configuration
8
+
9
+ Boolean-based configuration is intended to globally enable or disable a specific cookie attribute.
10
+
11
+ ```ruby
12
+ config.cookies = {
13
+ secure: true, # mark all cookies as Secure
14
+ httponly: false, # do not mark any cookies as HttpOnly
15
+ }
16
+ ```
17
+
18
+ #### Hash-based configuration
19
+
20
+ Hash-based configuration allows for fine-grained control.
21
+
22
+ ```ruby
23
+ config.cookies = {
24
+ secure: { except: ['_guest'] }, # mark all but the `_guest` cookie as Secure
25
+ httponly: { only: ['_rails_session'] }, # only mark the `_rails_session` cookie as HttpOnly
26
+ }
27
+ ```
28
+
29
+ #### SameSite cookie configuration
30
+
31
+ SameSite cookies permit either `Strict` or `Lax` enforcement mode options.
32
+
33
+ ```ruby
34
+ config.cookies = {
35
+ samesite: {
36
+ strict: true # mark all cookies as SameSite=Strict
37
+ }
38
+ }
39
+ ```
40
+
41
+ `Strict` and `Lax` enforcement modes can also be specified using a Hash.
42
+
43
+ ```ruby
44
+ config.cookies = {
45
+ samesite: {
46
+ strict: { only: ['_rails_session'] },
47
+ lax: { only: ['_guest'] }
48
+ }
49
+ }
50
+ ```
@@ -0,0 +1,64 @@
1
+ ## Hash
2
+
3
+ `script`/`style-src` hashes can be used to whitelist inline content that is static. This has the benefit of allowing inline content without opening up the possibility of dynamic javascript like you would with a `nonce`.
4
+
5
+ You can add hash sources directly to your policy :
6
+
7
+ ```ruby
8
+ ::SecureHeaders::Configuration.default do |config|
9
+ config.csp = {
10
+ default_src: %w('self')
11
+
12
+ # this is a made up value but browsers will show the expected hash in the console.
13
+ script_src: %w(sha256-123456)
14
+ }
15
+ end
16
+ ```
17
+
18
+ You can also use the automated inline script detection/collection/computation of hash source values in your app.
19
+
20
+ ```bash
21
+ rake secure_headers:generate_hashes
22
+ ```
23
+
24
+ This will generate a file (`config/config/secure_headers_generated_hashes.yml` by default, you can override by setting `ENV["secure_headers_generated_hashes_file"]`) containing a mapping of file names with the array of hash values found on that page. When ActionView renders a given file, we check if there are any known hashes for that given file. If so, they are added as values to the header.
25
+
26
+ ```yaml
27
+ ---
28
+ scripts:
29
+ app/views/asdfs/index.html.erb:
30
+ - "'sha256-yktKiAsZWmc8WpOyhnmhQoDf9G2dAZvuBBC+V0LGQhg='"
31
+ styles:
32
+ app/views/asdfs/index.html.erb:
33
+ - "'sha256-SLp6LO3rrKDJwsG9uJUxZapb4Wp2Zhj6Bu3l+d9rnAY='"
34
+ - "'sha256-HSGHqlRoKmHAGTAJ2Rq0piXX4CnEbOl1ArNd6ejp2TE='"
35
+ ```
36
+
37
+ ##### Helpers
38
+
39
+ **This will not compute dynamic hashes** by design. The output of both helpers will be a plain `script`/`style` tag without modification and the known hashes for a given file will be added to `script-src`/`style-src` when `hashed_javascript_tag` and `hashed_style_tag` are used. You can use `raise_error_on_unrecognized_hash = true` to be extra paranoid that you have precomputed hash values for all of your inline content. By default, this will raise an error in non-production environments.
40
+
41
+ ```erb
42
+ <%= hashed_style_tag do %>
43
+ body {
44
+ background-color: black;
45
+ }
46
+ <% end %>
47
+
48
+ <%= hashed_style_tag do %>
49
+ body {
50
+ font-size: 30px;
51
+ font-color: green;
52
+ }
53
+ <% end %>
54
+
55
+ <%= hashed_javascript_tag do %>
56
+ console.log(1)
57
+ <% end %>
58
+ ```
59
+
60
+ ```
61
+ Content-Security-Policy: ...
62
+ script-src 'sha256-yktKiAsZWmc8WpOyhnmhQoDf9G2dAZvuBBC+V0LGQhg=' ... ;
63
+ style-src 'sha256-SLp6LO3rrKDJwsG9uJUxZapb4Wp2Zhj6Bu3l+d9rnAY=' 'sha256-HSGHqlRoKmHAGTAJ2Rq0piXX4CnEbOl1ArNd6ejp2TE=' ...;
64
+ ```
@@ -0,0 +1,107 @@
1
+ ## Named Appends
2
+
3
+ Named Appends are blocks of code that can be reused and composed during requests. e.g. If a certain partial is rendered conditionally, and the csp needs to be adjusted for that partial, you can create a named append for that situation. The value returned by the block will be passed into `append_content_security_policy_directives`. The current request object is passed as an argument to the block for even more flexibility.
4
+
5
+ ```ruby
6
+ def show
7
+ if include_widget?
8
+ @widget = widget.render
9
+ use_content_security_policy_named_append(:widget_partial)
10
+ end
11
+ end
12
+
13
+
14
+ SecureHeaders::Configuration.named_append(:widget_partial) do |request|
15
+ SecureHeaders.override_x_frame_options(request, "DENY")
16
+ if request.controller_instance.current_user.in_test_bucket?
17
+ { child_src: %w(beta.thirdpartyhost.com) }
18
+ else
19
+ { child_src: %w(thirdpartyhost.com) }
20
+ end
21
+ end
22
+ ```
23
+
24
+ You can use as many named appends as you would like per request, but be careful because order of inclusion matters. Consider the following:
25
+
26
+ ```ruby
27
+ SecureHeader::Configuration.default do |config|
28
+ config.csp = { default_src: %w('self')}
29
+ end
30
+
31
+ SecureHeaders::Configuration.named_append(:A) do |request|
32
+ { default_src: %w(myhost.com) }
33
+ end
34
+
35
+ SecureHeaders::Configuration.named_append(:B) do |request|
36
+ { script_src: %w('unsafe-eval') }
37
+ end
38
+ ```
39
+
40
+ The following code will produce different policies due to the way policies are normalized (e.g. providing a previously undefined directive that inherits from `default-src`, removing host source values when `*` is provided. Removing `'none'` when additional values are present, etc.):
41
+
42
+ ```ruby
43
+ def index
44
+ use_content_security_policy_named_append(:A)
45
+ use_content_security_policy_named_append(:B)
46
+ # produces default-src 'self' myhost.com; script-src 'self' myhost.com 'unsafe-eval';
47
+ end
48
+
49
+ def show
50
+ use_content_security_policy_named_append(:B)
51
+ use_content_security_policy_named_append(:A)
52
+ # produces default-src 'self' myhost.com; script-src 'self' 'unsafe-eval';
53
+ end
54
+ ```
55
+
56
+
57
+ ## Named overrides
58
+
59
+ Named overrides serve two purposes:
60
+
61
+ * To be able to refer to a configuration by simple name.
62
+ * By precomputing the headers for a named configuration, the headers generated once and reused over every request.
63
+
64
+ To use a named override, drop a `SecureHeaders::Configuration.override` block **outside** of method definitions and then declare which named override you'd like to use. You can even override an override.
65
+
66
+ ```ruby
67
+ class ApplicationController < ActionController::Base
68
+ SecureHeaders::Configuration.default do |config|
69
+ config.csp = {
70
+ default_src: %w('self'),
71
+ script_src: %w(example.org)
72
+ }
73
+ end
74
+
75
+ # override default configuration
76
+ SecureHeaders::Configuration.override(:script_from_otherdomain_com) do |config|
77
+ config.csp[:script_src] << "otherdomain.com"
78
+ end
79
+
80
+ # overrides the :script_from_otherdomain_com configuration
81
+ SecureHeaders::Configuration.override(:another_config, :script_from_otherdomain_com) do |config|
82
+ config.csp[:script_src] << "evenanotherdomain.com"
83
+ end
84
+ end
85
+
86
+ class MyController < ApplicationController
87
+ def index
88
+ # Produces default-src 'self'; script-src example.org otherdomain.com
89
+ use_secure_headers_override(:script_from_otherdomain_com)
90
+ end
91
+
92
+ def show
93
+ # Produces default-src 'self'; script-src example.org otherdomain.org evenanotherdomain.com
94
+ use_secure_headers_override(:another_config)
95
+ end
96
+ end
97
+ ```
98
+
99
+ By default, a no-op configuration is provided. No headers will be set when this default override is used.
100
+
101
+ ```ruby
102
+ class MyController < ApplicationController
103
+ def index
104
+ SecureHeaders.opt_out_of_all_protection(request)
105
+ end
106
+ end
107
+ ```
@@ -0,0 +1,105 @@
1
+ ## Per-action configuration
2
+
3
+ You can override the settings for a given action by producing a temporary override. Be aware that because of the dynamic nature of the value, the header values will be computed per request.
4
+
5
+ ```ruby
6
+ # Given a config of:
7
+ ::SecureHeaders::Configuration.default do |config|
8
+ config.csp = {
9
+ default_src: %w('self'),
10
+ script_src: %w('self')
11
+ }
12
+ end
13
+
14
+ class MyController < ApplicationController
15
+ def index
16
+ # Append value to the source list, override 'none' values
17
+ # Produces: default-src 'self'; script-src 'self' s3.amazonaws.com; object-src 'self' www.youtube.com
18
+ append_content_security_policy_directives(script_src: %w(s3.amazonaws.com), object_src: %w('self' www.youtube.com))
19
+
20
+ # Overrides the previously set source list, override 'none' values
21
+ # Produces: default-src 'self'; script-src s3.amazonaws.com; object-src 'self'
22
+ override_content_security_policy_directives(script_src: %w(s3.amazonaws.com), object_src: %w('self'))
23
+
24
+ # Global settings default to "sameorigin"
25
+ override_x_frame_options("DENY")
26
+ end
27
+ ```
28
+
29
+ The following methods are available as controller instance methods. They are also available as class methods, but require you to pass in the `request` object.
30
+ * `append_content_security_policy_directives(hash)`: appends each value to the corresponding CSP app-wide configuration.
31
+ * `override_content_security_policy_directives(hash)`: merges the hash into the app-wide configuration, overwriting any previous config
32
+ * `override_x_frame_options(value)`: sets the `X-Frame-Options header` to `value`
33
+
34
+ ## Appending / overriding Content Security Policy
35
+
36
+ When manipulating content security policy, there are a few things to consider. The default header value is `default-src https:` which corresponds to a default configuration of `{ default_src: %w(https:)}`.
37
+
38
+ #### Append to the policy with a directive other than `default_src`
39
+
40
+ The value of `default_src` is joined with the addition if the it is a [fetch directive](https://w3c.github.io/webappsec-csp/#directives-fetch). Note the `https:` is carried over from the `default-src` config. If you do not want this, use `override_content_security_policy_directives` instead. To illustrate:
41
+
42
+ ```ruby
43
+ ::SecureHeaders::Configuration.default do |config|
44
+ config.csp = {
45
+ default_src: %w('self')
46
+ }
47
+ end
48
+ ```
49
+
50
+ Code | Result
51
+ ------------- | -------------
52
+ `append_content_security_policy_directives(script_src: %w(mycdn.com))` | `default-src 'self'; script-src 'self' mycdn.com`
53
+ `override_content_security_policy_directives(script_src: %w(mycdn.com))` | `default-src 'self'; script-src mycdn.com`
54
+
55
+ #### Nonce
56
+
57
+ You can use a view helper to automatically add nonces to script tags:
58
+
59
+ ```erb
60
+ <%= nonced_javascript_tag do %>
61
+ console.log("nonced!");
62
+ <% end %>
63
+
64
+ <%= nonced_style_tag do %>
65
+ body {
66
+ background-color: black;
67
+ }
68
+ <% end %>
69
+ ```
70
+
71
+ becomes:
72
+
73
+ ```html
74
+ <script nonce="/jRAxuLJsDXAxqhNBB7gg7h55KETtDQBXe4ZL+xIXwI=">
75
+ console.log("nonced!")
76
+ </script>
77
+ <style nonce="/jRAxuLJsDXAxqhNBB7gg7h55KETtDQBXe4ZL+xIXwI=">
78
+ body {
79
+ background-color: black;
80
+ }
81
+ </style>
82
+ ```
83
+
84
+ ```
85
+
86
+ Content-Security-Policy: ...
87
+ script-src 'nonce-/jRAxuLJsDXAxqhNBB7gg7h55KETtDQBXe4ZL+xIXwI=' ...;
88
+ style-src 'nonce-/jRAxuLJsDXAxqhNBB7gg7h55KETtDQBXe4ZL+xIXwI=' ...;
89
+ ```
90
+
91
+ `script`/`style-nonce` can be used to whitelist inline content. To do this, call the `content_security_policy_script_nonce` or `content_security_policy_style_nonce` then set the nonce attributes on the various tags.
92
+
93
+ ```erb
94
+ <script nonce="<%= content_security_policy_script_nonce %>">
95
+ console.log("whitelisted, will execute")
96
+ </script>
97
+
98
+ <script nonce="lol">
99
+ console.log("won't execute, not whitelisted")
100
+ </script>
101
+
102
+ <script>
103
+ console.log("won't execute, not whitelisted")
104
+ </script>
105
+ ```
@@ -0,0 +1,25 @@
1
+ ## Sinatra
2
+
3
+ Here's an example using SecureHeaders for Sinatra applications:
4
+
5
+ ```ruby
6
+ require 'rubygems'
7
+ require 'sinatra'
8
+ require 'haml'
9
+ require 'secure_headers'
10
+
11
+ use SecureHeaders::Middleware
12
+
13
+ SecureHeaders::Configuration.default do |config|
14
+ ...
15
+ end
16
+
17
+ class Donkey < Sinatra::Application
18
+ set :root, APP_ROOT
19
+
20
+ get '/' do
21
+ SecureHeaders.override_x_frame_options(request, SecureHeaders::OPT_OUT)
22
+ haml :index
23
+ end
24
+ end
25
+ ```
@@ -10,6 +10,7 @@ require "secure_headers/headers/x_content_type_options"
10
10
  require "secure_headers/headers/x_download_options"
11
11
  require "secure_headers/headers/x_permitted_cross_domain_policies"
12
12
  require "secure_headers/headers/referrer_policy"
13
+ require "secure_headers/headers/clear_site_data"
13
14
  require "secure_headers/middleware"
14
15
  require "secure_headers/railtie"
15
16
  require "secure_headers/view_helper"
@@ -50,6 +51,7 @@ module SecureHeaders
50
51
  CSP = ContentSecurityPolicy
51
52
 
52
53
  ALL_HEADER_CLASSES = [
54
+ ClearSiteData,
53
55
  ContentSecurityPolicyConfig,
54
56
  ContentSecurityPolicyReportOnlyConfig,
55
57
  StrictTransportSecurity,
@@ -116,7 +116,7 @@ module SecureHeaders
116
116
 
117
117
  attr_writer :hsts, :x_frame_options, :x_content_type_options,
118
118
  :x_xss_protection, :x_download_options, :x_permitted_cross_domain_policies,
119
- :referrer_policy
119
+ :referrer_policy, :clear_site_data
120
120
 
121
121
  attr_reader :cached_headers, :csp, :cookies, :csp_report_only, :hpkp, :hpkp_report_host
122
122
 
@@ -150,6 +150,7 @@ module SecureHeaders
150
150
  copy.x_xss_protection = @x_xss_protection
151
151
  copy.x_download_options = @x_download_options
152
152
  copy.x_permitted_cross_domain_policies = @x_permitted_cross_domain_policies
153
+ copy.clear_site_data = @clear_site_data
153
154
  copy.referrer_policy = @referrer_policy
154
155
  copy.hpkp = @hpkp
155
156
  copy.hpkp_report_host = @hpkp_report_host
@@ -181,6 +182,7 @@ module SecureHeaders
181
182
  XXssProtection.validate_config!(@x_xss_protection)
182
183
  XDownloadOptions.validate_config!(@x_download_options)
183
184
  XPermittedCrossDomainPolicies.validate_config!(@x_permitted_cross_domain_policies)
185
+ ClearSiteData.validate_config!(@clear_site_data)
184
186
  PublicKeyPins.validate_config!(@hpkp)
185
187
  Cookie.validate_config!(@cookies)
186
188
  end
@@ -0,0 +1,51 @@
1
+ module SecureHeaders
2
+ class ClearSiteDataConfigError < StandardError; end
3
+ class ClearSiteData
4
+ HEADER_NAME = "Clear-Site-Data".freeze
5
+ TYPES = "types".freeze
6
+
7
+ # Valid `types`
8
+ CACHE = "cache".freeze
9
+ COOKIES = "cookies".freeze
10
+ STORAGE = "storage".freeze
11
+ EXECTION_CONTEXTS = "executionContexts".freeze
12
+ ALL_TYPES = [CACHE, COOKIES, STORAGE, EXECTION_CONTEXTS]
13
+
14
+ CONFIG_KEY = :clear_site_data
15
+
16
+ class << self
17
+ # Public: make an Clear-Site-Data header name, value pair
18
+ #
19
+ # Returns nil if not configured, returns header name and value if configured.
20
+ def make_header(config=nil)
21
+ case config
22
+ when nil, OPT_OUT, []
23
+ # noop
24
+ when Array
25
+ [HEADER_NAME, JSON.dump(TYPES => config)]
26
+ when true
27
+ [HEADER_NAME, JSON.dump(TYPES => ALL_TYPES)]
28
+ end
29
+ end
30
+
31
+ def validate_config!(config)
32
+ case config
33
+ when nil, OPT_OUT, true
34
+ # valid
35
+ when Array
36
+ unless config.all? { |t| t.is_a?(String) }
37
+ raise ClearSiteDataConfigError.new("types must be Strings")
38
+ end
39
+
40
+ begin
41
+ JSON.dump(config)
42
+ rescue JSON::GeneratorError, Encoding::UndefinedConversionError
43
+ raise ClearSiteDataConfigError.new("types must serializable by JSON")
44
+ end
45
+ else
46
+ raise ClearSiteDataConfigError.new("config must be an Array of Strings or `true`")
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
@@ -1,7 +1,7 @@
1
1
  # -*- encoding: utf-8 -*-
2
2
  Gem::Specification.new do |gem|
3
3
  gem.name = "secure_headers"
4
- gem.version = "3.5.1"
4
+ gem.version = "3.6.0"
5
5
  gem.authors = ["Neil Matatall"]
6
6
  gem.email = ["neil.matatall@gmail.com"]
7
7
  gem.description = 'Security related headers all in one gem.'
@@ -0,0 +1,103 @@
1
+ require 'spec_helper'
2
+
3
+ module SecureHeaders
4
+ describe ClearSiteData do
5
+ describe "make_header" do
6
+ it "returns nil with nil config" do
7
+ expect(described_class.make_header).to be_nil
8
+ end
9
+
10
+ it "returns nil with empty config" do
11
+ expect(described_class.make_header([])).to be_nil
12
+ end
13
+
14
+ it "returns nil with opt-out config" do
15
+ expect(described_class.make_header(OPT_OUT)).to be_nil
16
+ end
17
+
18
+ it "returns all types with `true` config" do
19
+ name, value = described_class.make_header(true)
20
+
21
+ expect(name).to eq(ClearSiteData::HEADER_NAME)
22
+ expect(value).to eq(normalize_json(<<-HERE))
23
+ {
24
+ "types": [
25
+ "cache",
26
+ "cookies",
27
+ "storage",
28
+ "executionContexts"
29
+ ]
30
+ }
31
+ HERE
32
+ end
33
+
34
+ it "returns specified types" do
35
+ name, value = described_class.make_header(["foo", "bar"])
36
+
37
+ expect(name).to eq(ClearSiteData::HEADER_NAME)
38
+ expect(value).to eq(normalize_json(<<-HERE))
39
+ {
40
+ "types": [
41
+ "foo",
42
+ "bar"
43
+ ]
44
+ }
45
+ HERE
46
+ end
47
+ end
48
+
49
+ describe "validate_config!" do
50
+ it "succeeds for `true` config" do
51
+ expect do
52
+ described_class.validate_config!(true)
53
+ end.not_to raise_error
54
+ end
55
+
56
+ it "succeeds for `nil` config" do
57
+ expect do
58
+ described_class.validate_config!(nil)
59
+ end.not_to raise_error
60
+ end
61
+
62
+ it "succeeds for opt-out config" do
63
+ expect do
64
+ described_class.validate_config!(OPT_OUT)
65
+ end.not_to raise_error
66
+ end
67
+
68
+ it "succeeds for empty config" do
69
+ expect do
70
+ described_class.validate_config!([])
71
+ end.not_to raise_error
72
+ end
73
+
74
+ it "succeeds for Array of Strings config" do
75
+ expect do
76
+ described_class.validate_config!(["foo"])
77
+ end.not_to raise_error
78
+ end
79
+
80
+ it "fails for Array of non-String config" do
81
+ expect do
82
+ described_class.validate_config!([1])
83
+ end.to raise_error(ClearSiteDataConfigError)
84
+ end
85
+
86
+ it "fails for non-serializable config" do
87
+ expect do
88
+ described_class.validate_config!(["hi \255"])
89
+ end.to raise_error(ClearSiteDataConfigError)
90
+ end
91
+
92
+ it "fails for other types of config" do
93
+ expect do
94
+ described_class.validate_config!(:cookies)
95
+ end.to raise_error(ClearSiteDataConfigError)
96
+ end
97
+ end
98
+
99
+ def normalize_json(json)
100
+ JSON.dump(JSON.parse(json))
101
+ end
102
+ end
103
+ end
@@ -530,6 +530,14 @@ module SecureHeaders
530
530
  end.to raise_error(XContentTypeOptionsConfigError)
531
531
  end
532
532
 
533
+ it "validates your clear site data config upon configuration" do
534
+ expect do
535
+ Configuration.default do |config|
536
+ config.clear_site_data = 1
537
+ end
538
+ end.to raise_error(ClearSiteDataConfigError)
539
+ end
540
+
533
541
  it "validates your x_xss config upon configuration" do
534
542
  expect do
535
543
  Configuration.default do |config|
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: secure_headers
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.5.1
4
+ version: 3.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Neil Matatall
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-11-11 00:00:00.000000000 Z
11
+ date: 2017-01-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
@@ -58,9 +58,16 @@ files:
58
58
  - LICENSE
59
59
  - README.md
60
60
  - Rakefile
61
+ - docs/HPKP.md
62
+ - docs/cookies.md
63
+ - docs/hashes.md
64
+ - docs/named_overrides_and_appends.md
65
+ - docs/per_action_configuration.md
66
+ - docs/sinatra.md
61
67
  - lib/secure_headers.rb
62
68
  - lib/secure_headers/configuration.rb
63
69
  - lib/secure_headers/hash_helper.rb
70
+ - lib/secure_headers/headers/clear_site_data.rb
64
71
  - lib/secure_headers/headers/content_security_policy.rb
65
72
  - lib/secure_headers/headers/content_security_policy_config.rb
66
73
  - lib/secure_headers/headers/cookie.rb
@@ -80,6 +87,7 @@ files:
80
87
  - lib/tasks/tasks.rake
81
88
  - secure_headers.gemspec
82
89
  - spec/lib/secure_headers/configuration_spec.rb
90
+ - spec/lib/secure_headers/headers/clear_site_data_spec.rb
83
91
  - spec/lib/secure_headers/headers/content_security_policy_spec.rb
84
92
  - spec/lib/secure_headers/headers/cookie_spec.rb
85
93
  - spec/lib/secure_headers/headers/policy_management_spec.rb
@@ -123,6 +131,7 @@ summary: Add easily configured security headers to responses including content-s
123
131
  x-frame-options, strict-transport-security, etc.
124
132
  test_files:
125
133
  - spec/lib/secure_headers/configuration_spec.rb
134
+ - spec/lib/secure_headers/headers/clear_site_data_spec.rb
126
135
  - spec/lib/secure_headers/headers/content_security_policy_spec.rb
127
136
  - spec/lib/secure_headers/headers/cookie_spec.rb
128
137
  - spec/lib/secure_headers/headers/policy_management_spec.rb