secure_headers 3.4.0 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. checksums.yaml +4 -4
  2. data/.rspec +2 -0
  3. data/.rubocop.yml +3 -0
  4. data/.ruby-version +1 -1
  5. data/.travis.yml +8 -4
  6. data/CHANGELOG.md +113 -1
  7. data/CODE_OF_CONDUCT.md +46 -0
  8. data/CONTRIBUTING.md +41 -0
  9. data/Gemfile +8 -4
  10. data/Guardfile +1 -0
  11. data/LICENSE +4 -199
  12. data/README.md +63 -333
  13. data/Rakefile +22 -18
  14. data/docs/HPKP.md +17 -0
  15. data/docs/cookies.md +64 -0
  16. data/docs/hashes.md +64 -0
  17. data/docs/named_overrides_and_appends.md +107 -0
  18. data/docs/per_action_configuration.md +133 -0
  19. data/docs/sinatra.md +25 -0
  20. data/lib/secure_headers/configuration.rb +91 -44
  21. data/lib/secure_headers/hash_helper.rb +2 -1
  22. data/lib/secure_headers/headers/clear_site_data.rb +55 -0
  23. data/lib/secure_headers/headers/content_security_policy.rb +110 -51
  24. data/lib/secure_headers/headers/content_security_policy_config.rb +162 -0
  25. data/lib/secure_headers/headers/cookie.rb +21 -3
  26. data/lib/secure_headers/headers/expect_certificate_transparency.rb +70 -0
  27. data/lib/secure_headers/headers/policy_management.rb +143 -69
  28. data/lib/secure_headers/headers/public_key_pins.rb +5 -4
  29. data/lib/secure_headers/headers/referrer_policy.rb +5 -1
  30. data/lib/secure_headers/headers/strict_transport_security.rb +2 -1
  31. data/lib/secure_headers/headers/x_content_type_options.rb +2 -1
  32. data/lib/secure_headers/headers/x_download_options.rb +3 -2
  33. data/lib/secure_headers/headers/x_frame_options.rb +2 -1
  34. data/lib/secure_headers/headers/x_permitted_cross_domain_policies.rb +3 -2
  35. data/lib/secure_headers/headers/x_xss_protection.rb +2 -1
  36. data/lib/secure_headers/middleware.rb +10 -9
  37. data/lib/secure_headers/railtie.rb +7 -6
  38. data/lib/secure_headers/utils/cookies_config.rb +13 -12
  39. data/lib/secure_headers/view_helper.rb +12 -3
  40. data/lib/secure_headers.rb +137 -55
  41. data/lib/tasks/tasks.rake +2 -1
  42. data/secure_headers.gemspec +13 -3
  43. data/spec/lib/secure_headers/configuration_spec.rb +13 -12
  44. data/spec/lib/secure_headers/headers/clear_site_data_spec.rb +87 -0
  45. data/spec/lib/secure_headers/headers/content_security_policy_spec.rb +59 -26
  46. data/spec/lib/secure_headers/headers/cookie_spec.rb +38 -20
  47. data/spec/lib/secure_headers/headers/expect_certificate_spec.rb +42 -0
  48. data/spec/lib/secure_headers/headers/policy_management_spec.rb +78 -40
  49. data/spec/lib/secure_headers/headers/public_key_pins_spec.rb +7 -6
  50. data/spec/lib/secure_headers/headers/referrer_policy_spec.rb +22 -3
  51. data/spec/lib/secure_headers/headers/strict_transport_security_spec.rb +5 -4
  52. data/spec/lib/secure_headers/headers/x_content_type_options_spec.rb +2 -1
  53. data/spec/lib/secure_headers/headers/x_download_options_spec.rb +3 -2
  54. data/spec/lib/secure_headers/headers/x_frame_options_spec.rb +2 -1
  55. data/spec/lib/secure_headers/headers/x_permitted_cross_domain_policies_spec.rb +4 -3
  56. data/spec/lib/secure_headers/headers/x_xss_protection_spec.rb +4 -3
  57. data/spec/lib/secure_headers/middleware_spec.rb +39 -19
  58. data/spec/lib/secure_headers/view_helpers_spec.rb +21 -8
  59. data/spec/lib/secure_headers_spec.rb +304 -56
  60. data/spec/spec_helper.rb +13 -24
  61. data/upgrading-to-4-0.md +55 -0
  62. metadata +29 -7
data/README.md CHANGED
@@ -1,12 +1,16 @@
1
- # Secure Headers [![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)
1
+ # Secure Headers [![Build Status](https://travis-ci.org/twitter/secureheaders.svg?branch=master)](http://travis-ci.org/twitter/secureheaders) [![Code Climate](https://codeclimate.com/github/twitter/secureheaders.svg)](https://codeclimate.com/github/twitter/secureheaders) [![Coverage Status](https://coveralls.io/repos/twitter/secureheaders/badge.svg)](https://coveralls.io/r/twitter/secureheaders)
2
2
 
3
+ **master represents the unreleased 4.x line**. See the [upgrading to 4.x doc](upgrading-to-4-0.md) for instructions on how to upgrade. Bug fixes should go in the 3.x branch for now.
3
4
 
4
- **The 3.x branch was recently merged**. See the [upgrading to 3.x doc](upgrading-to-3-0.md) for instructions on how to upgrade including the differences and benefits of using the 3.x branch.
5
+ **The [3.x](https://github.com/twitter/secureheaders/tree/2.x) branch is moving into maintenance mode**. See the [upgrading to 3.x doc](upgrading-to-3-0.md) for instructions on how to upgrade including the differences and benefits of using the 3.x branch.
5
6
 
6
- **The [2.x branch](https://github.com/twitter/secureheaders/tree/2.x) will be maintained**. The documentation below only applies to the 3.x branch. See the 2.x [README](https://github.com/twitter/secureheaders/blob/2.x/README.md) for the old way of doing things.
7
+ **The [2.x branch](https://github.com/twitter/secureheaders/tree/2.x) will be not be maintained once 4.x is released**. The documentation below only applies to the 3.x branch. See the 2.x [README](https://github.com/twitter/secureheaders/blob/2.x/README.md) for the old way of doing things.
7
8
 
8
9
  The gem will automatically apply several headers that are related to security. This includes:
9
10
  - Content Security Policy (CSP) - Helps detect/prevent XSS, mixed-content, and other classes of attack. [CSP 2 Specification](http://www.w3.org/TR/CSP2/)
11
+ - https://csp.withgoogle.com
12
+ - https://csp.withgoogle.com/docs/strict-csp.html
13
+ - https://csp-evaluator.withgoogle.com
10
14
  - HTTP Strict Transport Security (HSTS) - Ensures the browser never visits the http version of a website. Protects from SSLStrip/Firesheep attacks. [HSTS Specification](https://tools.ietf.org/html/rfc6797)
11
15
  - X-Frame-Options (XFO) - Prevents your content from being framed and potentially clickjacked. [X-Frame-Options Specification](https://tools.ietf.org/html/rfc7034)
12
16
  - X-XSS-Protection - [Cross site scripting heuristic filter for IE/Chrome](https://msdn.microsoft.com/en-us/library/dd565647\(v=vs.85\).aspx)
@@ -15,14 +19,35 @@ The gem will automatically apply several headers that are related to security.
15
19
  - 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)
16
20
  - Referrer-Policy - [Referrer Policy draft](https://w3c.github.io/webappsec-referrer-policy/)
17
21
  - 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)
22
+ - Expect-CT - Only use certificates that are present in the certificate transparency logs. [Expect-CT draft specification](https://datatracker.ietf.org/doc/draft-stark-expect-ct/).
23
+ - Clear-Site-Data - Clearing browser data for origin. [Clear-Site-Data specification](https://w3c.github.io/webappsec-clear-site-data/).
18
24
 
19
25
  It can also mark all http cookies with the Secure, HttpOnly and SameSite attributes (when configured to do so).
20
26
 
21
27
  `secure_headers` is a library with a global config, per request overrides, and rack middleware that enables you customize your application settings.
22
28
 
23
- ## Use
29
+ ## Documentation
24
30
 
25
- `gem install secure_headers`
31
+ - [Named overrides and appends](docs/named_overrides_and_appends.md)
32
+ - [Per action configuration](docs/per_action_configuration.md)
33
+ - [Cookies](docs/cookies.md)
34
+ - [HPKP](docs/HPKP.md)
35
+ - [Hashes](docs/hashes.md)
36
+ - [Sinatra Config](docs/sinatra.md)
37
+
38
+ ## Getting Started
39
+
40
+ ### Rails 3+
41
+
42
+ 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.
43
+
44
+ ### Rails 2
45
+
46
+ For Rails 2 or non-rails applications, an explicit statement is required to use the middleware component.
47
+
48
+ ```ruby
49
+ use SecureHeaders::Middleware
50
+ ```
26
51
 
27
52
  ## Configuration
28
53
 
@@ -30,25 +55,38 @@ If you do not supply a `default` configuration, exceptions will be raised. If yo
30
55
 
31
56
  All `nil` values will fallback to their default values. `SecureHeaders::OPT_OUT` will disable the header entirely.
32
57
 
58
+ **Word of caution:** The following is not a default configuration per se. It serves as a sample implementation of the configuration. You should read more about these headers and determine what is appropriate for your requirements.
59
+
33
60
  ```ruby
34
61
  SecureHeaders::Configuration.default do |config|
35
62
  config.cookies = {
36
63
  secure: true, # mark all cookies as "Secure"
37
64
  httponly: true, # mark all cookies as "HttpOnly"
38
65
  samesite: {
39
- strict: true # mark all cookies as SameSite=Strict
66
+ lax: true # mark all cookies as SameSite=lax
40
67
  }
41
68
  }
42
- config.hsts = "max-age=#{20.years.to_i}; includeSubdomains; preload"
69
+ # Add "; preload" and submit the site to hstspreload.org for best protection.
70
+ config.hsts = "max-age=#{20.years.to_i}; includeSubdomains"
43
71
  config.x_frame_options = "DENY"
44
72
  config.x_content_type_options = "nosniff"
45
73
  config.x_xss_protection = "1; mode=block"
46
74
  config.x_download_options = "noopen"
47
75
  config.x_permitted_cross_domain_policies = "none"
48
76
  config.referrer_policy = "origin-when-cross-origin"
77
+ config.clear_site_data = [
78
+ "cache",
79
+ "cookies",
80
+ "storage",
81
+ "executionContexts"
82
+ ]
83
+ config.expect_certificate_transparency = {
84
+ enforce: false,
85
+ max_age: 1.day.to_i,
86
+ report_uri: "https://report-uri.io/example-ct"
87
+ }
49
88
  config.csp = {
50
- # "meta" values. these will shaped the header, but the values are not included in the header.
51
- report_only: true, # default: false
89
+ # "meta" values. these will shape the header, but the values are not included in the header.
52
90
  preserve_schemes: true, # default: false. Schemes are removed from host sources to save bytes and discourage mixed content.
53
91
 
54
92
  # directive values: these values will directly translate into source directives
@@ -61,14 +99,21 @@ SecureHeaders::Configuration.default do |config|
61
99
  form_action: %w('self' github.com),
62
100
  frame_ancestors: %w('none'),
63
101
  img_src: %w(mycdn.com data:),
102
+ manifest_src: %w('self'),
64
103
  media_src: %w(utoob.com),
65
104
  object_src: %w('self'),
105
+ sandbox: true, # true and [] will set a maximally restrictive setting
66
106
  plugin_types: %w(application/x-shockwave-flash),
67
107
  script_src: %w('self'),
68
108
  style_src: %w('unsafe-inline'),
69
109
  upgrade_insecure_requests: true, # see https://www.w3.org/TR/upgrade-insecure-requests/
70
110
  report_uri: %w(https://report-uri.io/example-csp)
71
111
  }
112
+ # This is available only from 3.5.0; use the `report_only: true` setting for 3.4.1 and below.
113
+ config.csp_report_only = config.csp.merge({
114
+ img_src: %w(somewhereelse.com),
115
+ report_uri: %w(https://report-uri.io/example-csp-report-only)
116
+ })
72
117
  config.hpkp = {
73
118
  report_only: false,
74
119
  max_age: 60.days.to_i,
@@ -82,334 +127,18 @@ SecureHeaders::Configuration.default do |config|
82
127
  end
83
128
  ```
84
129
 
85
- ### rails 2
86
-
87
- 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.
88
-
89
- ```ruby
90
- use SecureHeaders::Middleware
91
- ```
92
-
93
130
  ## Default values
94
131
 
95
- 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).
96
-
97
- ## Named overrides
98
-
99
- Named overrides serve two purposes:
100
-
101
- * To be able to refer to a configuration by simple name.
102
- * By precomputing the headers for a named configuration, the headers generated once and reused over every request.
103
-
104
- 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.
105
-
106
- ```ruby
107
- class ApplicationController < ActionController::Base
108
- SecureHeaders::Configuration.default do |config|
109
- config.csp = {
110
- default_src: %w('self'),
111
- script_src: %w(example.org)
112
- }
113
- end
114
-
115
- # override default configuration
116
- SecureHeaders::Configuration.override(:script_from_otherdomain_com) do |config|
117
- config.csp[:script_src] << "otherdomain.com"
118
- end
119
-
120
- # overrides the :script_from_otherdomain_com configuration
121
- SecureHeaders::Configuration.override(:another_config, :script_from_otherdomain_com) do |config|
122
- config.csp[:script_src] << "evenanotherdomain.com"
123
- end
124
- end
125
-
126
- class MyController < ApplicationController
127
- def index
128
- # Produces default-src 'self'; script-src example.org otherdomain.org
129
- use_secure_headers_override(:script_from_otherdomain_com)
130
- end
131
-
132
- def show
133
- # Produces default-src 'self'; script-src example.org otherdomain.org evenanotherdomain.com
134
- use_secure_headers_override(:another_config)
135
- end
136
- end
137
- ```
138
-
139
- By default, a no-op configuration is provided. No headers will be set when this default override is used.
140
-
141
- ```ruby
142
- class MyController < ApplicationController
143
- def index
144
- SecureHeaders.opt_out_of_all_protection(request)
145
- end
146
- end
147
- ```
148
-
149
- ## Per-action configuration
150
-
151
- 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.
152
-
153
- ```ruby
154
- # Given a config of:
155
- ::SecureHeaders::Configuration.default do |config|
156
- config.csp = {
157
- default_src: %w('self'),
158
- script_src: %w('self')
159
- }
160
- end
161
-
162
- class MyController < ApplicationController
163
- def index
164
- # Append value to the source list, override 'none' values
165
- # Produces: default-src 'self'; script-src 'self' s3.amazonaws.com; object-src 'self' www.youtube.com
166
- append_content_security_policy_directives(script_src: %w(s3.amazonaws.com), object_src: %w('self' www.youtube.com))
167
-
168
- # Overrides the previously set source list, override 'none' values
169
- # Produces: default-src 'self'; script-src s3.amazonaws.com; object-src 'self'
170
- override_content_security_policy_directives(script_src: %w(s3.amazonaws.com), object_src: %w('self'))
171
-
172
- # Global settings default to "sameorigin"
173
- override_x_frame_options("DENY")
174
- end
175
- ```
176
-
177
- 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.
178
- * `append_content_security_policy_directives(hash)`: appends each value to the corresponding CSP app-wide configuration.
179
- * `override_content_security_policy_directives(hash)`: merges the hash into the app-wide configuration, overwriting any previous config
180
- * `override_x_frame_options(value)`: sets the `X-Frame-Options header` to `value`
181
-
182
- ## Appending / overriding Content Security Policy
183
-
184
- 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:)}`.
185
-
186
- #### Append to the policy with a directive other than `default_src`
187
-
188
- 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:
189
-
190
- ```ruby
191
- ::SecureHeaders::Configuration.default do |config|
192
- config.csp = {
193
- default_src: %w('self')
194
- }
195
- end
196
- ```
197
-
198
- Code | Result
199
- ------------- | -------------
200
- `append_content_security_policy_directives(script_src: %w(mycdn.com))` | `default-src 'self'; script-src 'self' mycdn.com`
201
- `override_content_security_policy_directives(script_src: %w(mycdn.com))` | `default-src 'self'; script-src mycdn.com`
202
-
203
- #### Nonce
204
-
205
- You can use a view helper to automatically add nonces to script tags:
206
-
207
- ```erb
208
- <%= nonced_javascript_tag do %>
209
- console.log("nonced!");
210
- <% end %>
211
-
212
- <%= nonced_style_tag do %>
213
- body {
214
- background-color: black;
215
- }
216
- <% end %>
217
- ```
218
-
219
- becomes:
220
-
221
- ```html
222
- <script nonce="/jRAxuLJsDXAxqhNBB7gg7h55KETtDQBXe4ZL+xIXwI=">
223
- console.log("nonced!")
224
- </script>
225
- <style nonce="/jRAxuLJsDXAxqhNBB7gg7h55KETtDQBXe4ZL+xIXwI=">
226
- body {
227
- background-color: black;
228
- }
229
- </style>
230
- ```
231
-
232
- ```
233
-
234
- Content-Security-Policy: ...
235
- script-src 'nonce-/jRAxuLJsDXAxqhNBB7gg7h55KETtDQBXe4ZL+xIXwI=' ...;
236
- style-src 'nonce-/jRAxuLJsDXAxqhNBB7gg7h55KETtDQBXe4ZL+xIXwI=' ...;
237
- ```
238
-
239
- `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.
240
-
241
- ```erb
242
- <script nonce="<%= content_security_policy_script_nonce %>">
243
- console.log("whitelisted, will execute")
244
- </script>
245
-
246
- <script nonce="lol">
247
- console.log("won't execute, not whitelisted")
248
- </script>
249
-
250
- <script>
251
- console.log("won't execute, not whitelisted")
252
- </script>
253
- ```
254
-
255
- #### Hash
256
-
257
- `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`.
258
-
259
- You can add hash sources directly to your policy :
260
-
261
- ```ruby
262
- ::SecureHeaders::Configuration.default do |config|
263
- config.csp = {
264
- default_src: %w('self')
265
-
266
- # this is a made up value but browsers will show the expected hash in the console.
267
- script_src: %w(sha256-123456)
268
- }
269
- end
270
- ```
271
-
272
- You can also use the automated inline script detection/collection/computation of hash source values in your app.
273
-
274
- ```bash
275
- rake secure_headers:generate_hashes
276
- ```
277
-
278
- 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.
279
-
280
- ```yaml
281
- ---
282
- scripts:
283
- app/views/asdfs/index.html.erb:
284
- - "'sha256-yktKiAsZWmc8WpOyhnmhQoDf9G2dAZvuBBC+V0LGQhg='"
285
- styles:
286
- app/views/asdfs/index.html.erb:
287
- - "'sha256-SLp6LO3rrKDJwsG9uJUxZapb4Wp2Zhj6Bu3l+d9rnAY='"
288
- - "'sha256-HSGHqlRoKmHAGTAJ2Rq0piXX4CnEbOl1ArNd6ejp2TE='"
289
- ```
290
-
291
- ##### Helpers
292
-
293
- **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.
294
-
295
- ```erb
296
- <%= hashed_style_tag do %>
297
- body {
298
- background-color: black;
299
- }
300
- <% end %>
301
-
302
- <%= hashed_style_tag do %>
303
- body {
304
- font-size: 30px;
305
- font-color: green;
306
- }
307
- <% end %>
308
-
309
- <%= hashed_javascript_tag do %>
310
- console.log(1)
311
- <% end %>
312
- ```
313
-
314
- ```
315
- Content-Security-Policy: ...
316
- script-src 'sha256-yktKiAsZWmc8WpOyhnmhQoDf9G2dAZvuBBC+V0LGQhg=' ... ;
317
- style-src 'sha256-SLp6LO3rrKDJwsG9uJUxZapb4Wp2Zhj6Bu3l+d9rnAY=' 'sha256-HSGHqlRoKmHAGTAJ2Rq0piXX4CnEbOl1ArNd6ejp2TE=' ...;
318
- ```
319
-
320
- ### Public Key Pins
321
-
322
- 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.
323
-
324
- ```ruby
325
- config.hpkp = {
326
- max_age: 60.days.to_i, # max_age is a required parameter
327
- include_subdomains: true, # whether or not to apply pins to subdomains
328
- # Per the spec, SHA256 hashes are the only currently supported format.
329
- pins: [
330
- {sha256: 'b5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b878ae4944c'},
331
- {sha256: '73a2c64f9545172c1195efb6616ca5f7afd1df6f245407cafb90de3998a1c97f'}
332
- ],
333
- report_only: true, # defaults to false (report-only mode)
334
- report_uri: 'https://report-uri.io/example-hpkp'
335
- }
336
- ```
337
-
338
- ### Cookies
339
-
340
- 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.
132
+ All headers except for PublicKeyPins and ClearSiteData have a default value. The default set of headers is:
341
133
 
342
- __Note__: Regardless of the configuration specified, Secure cookies are only enabled for HTTPS requests.
343
-
344
- #### Boolean-based configuration
345
-
346
- Boolean-based configuration is intended to globally enable or disable a specific cookie attribute.
347
-
348
- ```ruby
349
- config.cookies = {
350
- secure: true, # mark all cookies as Secure
351
- httponly: false, # do not mark any cookies as HttpOnly
352
- }
353
- ```
354
-
355
- #### Hash-based configuration
356
-
357
- Hash-based configuration allows for fine-grained control.
358
-
359
- ```ruby
360
- config.cookies = {
361
- secure: { except: ['_guest'] }, # mark all but the `_guest` cookie as Secure
362
- httponly: { only: ['_rails_session'] }, # only mark the `_rails_session` cookie as HttpOnly
363
- }
364
- ```
365
-
366
- #### SameSite cookie configuration
367
-
368
- SameSite cookies permit either `Strict` or `Lax` enforcement mode options.
369
-
370
- ```ruby
371
- config.cookies = {
372
- samesite: {
373
- strict: true # mark all cookies as SameSite=Strict
374
- }
375
- }
376
- ```
377
-
378
- `Strict` and `Lax` enforcement modes can also be specified using a Hash.
379
-
380
- ```ruby
381
- config.cookies = {
382
- samesite: {
383
- strict: { only: ['_rails_session'] },
384
- lax: { only: ['_guest'] }
385
- }
386
- }
387
134
  ```
388
-
389
- ### Using with Sinatra
390
-
391
- Here's an example using SecureHeaders for Sinatra applications:
392
-
393
- ```ruby
394
- require 'rubygems'
395
- require 'sinatra'
396
- require 'haml'
397
- require 'secure_headers'
398
-
399
- use SecureHeaders::Middleware
400
-
401
- SecureHeaders::Configuration.default do |config|
402
- ...
403
- end
404
-
405
- class Donkey < Sinatra::Application
406
- set :root, APP_ROOT
407
-
408
- get '/' do
409
- SecureHeaders.override_x_frame_options(request, SecureHeaders::OPT_OUT)
410
- haml :index
411
- end
412
- end
135
+ 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'
136
+ Strict-Transport-Security: max-age=631138519
137
+ X-Content-Type-Options: nosniff
138
+ X-Download-Options: noopen
139
+ X-Frame-Options: sameorigin
140
+ X-Permitted-Cross-Domain-Policies: none
141
+ X-Xss-Protection: 1; mode=block
413
142
  ```
414
143
 
415
144
  ## Similar libraries
@@ -424,6 +153,7 @@ end
424
153
  * Elixir [secure_headers](https://github.com/anotherhale/secure_headers)
425
154
  * Dropwizard [dropwizard-web-security](https://github.com/palantir/dropwizard-web-security)
426
155
  * Ember.js [ember-cli-content-security-policy](https://github.com/rwjblue/ember-cli-content-security-policy/)
156
+ * PHP [secure-headers](https://github.com/BePsvPT/secure-headers)
427
157
 
428
158
  ## License
429
159
 
data/Rakefile CHANGED
@@ -1,28 +1,32 @@
1
1
  #!/usr/bin/env rake
2
- require 'bundler/gem_tasks'
3
- require 'rspec/core/rake_task'
4
- require 'net/http'
5
- require 'net/https'
2
+ # frozen_string_literal: true
3
+ require "bundler/gem_tasks"
4
+ require "rspec/core/rake_task"
5
+ require "net/http"
6
+ require "net/https"
6
7
 
7
- desc "Run RSpec"
8
- RSpec::Core::RakeTask.new do |t|
9
- t.verbose = false
10
- t.rspec_opts = "--format progress"
11
- end
12
-
13
- task default: :spec
8
+ RSpec::Core::RakeTask.new
14
9
 
15
10
  begin
16
- require 'rdoc/task'
11
+ require "rdoc/task"
17
12
  rescue LoadError
18
- require 'rdoc/rdoc'
19
- require 'rake/rdoctask'
13
+ require "rdoc/rdoc"
14
+ require "rake/rdoctask"
20
15
  RDoc::Task = Rake::RDocTask
21
16
  end
22
17
 
18
+ begin
19
+ require "rubocop/rake_task"
20
+ RuboCop::RakeTask.new
21
+ rescue LoadError
22
+ task(:rubocop) { $stderr.puts "RuboCop is disabled" }
23
+ end
24
+
23
25
  RDoc::Task.new(:rdoc) do |rdoc|
24
- rdoc.rdoc_dir = 'rdoc'
25
- rdoc.title = 'SecureHeaders'
26
- rdoc.options << '--line-numbers'
27
- rdoc.rdoc_files.include('lib/**/*.rb')
26
+ rdoc.rdoc_dir = "rdoc"
27
+ rdoc.title = "SecureHeaders"
28
+ rdoc.options << "--line-numbers"
29
+ rdoc.rdoc_files.include("lib/**/*.rb")
28
30
  end
31
+
32
+ task default: [:spec, :rubocop]
data/docs/HPKP.md ADDED
@@ -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
+ ```
data/docs/cookies.md ADDED
@@ -0,0 +1,64 @@
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
+ #### Defaults
8
+
9
+ By default, all cookies will get both `Secure`, `HttpOnly`, and `SameSite=Lax`.
10
+
11
+ ```ruby
12
+ config.cookies = {
13
+ secure: true, # defaults to true but will be a no op on non-HTTPS requests
14
+ httponly: true, # defaults to true
15
+ samesite: { # defaults to set `SameSite=Lax`
16
+ lax: true
17
+ }
18
+ }
19
+ ```
20
+
21
+ #### Boolean-based configuration
22
+
23
+ Boolean-based configuration is intended to globally enable or disable a specific cookie attribute. *Note: As of 4.0, you must use OPT_OUT rather than false to opt out of the defaults.*
24
+
25
+ ```ruby
26
+ config.cookies = {
27
+ secure: true, # mark all cookies as Secure
28
+ httponly: OPT_OUT, # do not mark any cookies as HttpOnly
29
+ }
30
+ ```
31
+
32
+ #### Hash-based configuration
33
+
34
+ Hash-based configuration allows for fine-grained control.
35
+
36
+ ```ruby
37
+ config.cookies = {
38
+ secure: { except: ['_guest'] }, # mark all but the `_guest` cookie as Secure
39
+ httponly: { only: ['_rails_session'] }, # only mark the `_rails_session` cookie as HttpOnly
40
+ }
41
+ ```
42
+
43
+ #### SameSite cookie configuration
44
+
45
+ SameSite cookies permit either `Strict` or `Lax` enforcement mode options.
46
+
47
+ ```ruby
48
+ config.cookies = {
49
+ samesite: {
50
+ strict: true # mark all cookies as SameSite=Strict
51
+ }
52
+ }
53
+ ```
54
+
55
+ `Strict` and `Lax` enforcement modes can also be specified using a Hash.
56
+
57
+ ```ruby
58
+ config.cookies = {
59
+ samesite: {
60
+ strict: { only: ['_rails_session'] },
61
+ lax: { only: ['_guest'] }
62
+ }
63
+ }
64
+ ```
data/docs/hashes.md ADDED
@@ -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/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
+ ```