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