secure_headers 2.5.3 → 3.0.0.pre

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of secure_headers might be problematic. Click here for more details.

Files changed (125) hide show
  1. checksums.yaml +4 -4
  2. data/.rspec +1 -0
  3. data/.travis.yml +2 -1
  4. data/Gemfile +9 -16
  5. data/README.md +154 -331
  6. data/Rakefile +2 -36
  7. data/lib/secure_headers/configuration.rb +189 -0
  8. data/lib/secure_headers/headers/content_security_policy.rb +341 -254
  9. data/lib/secure_headers/headers/public_key_pins.rb +43 -58
  10. data/lib/secure_headers/headers/strict_transport_security.rb +21 -49
  11. data/lib/secure_headers/headers/x_content_type_options.rb +18 -33
  12. data/lib/secure_headers/headers/x_download_options.rb +18 -33
  13. data/lib/secure_headers/headers/x_frame_options.rb +24 -34
  14. data/lib/secure_headers/headers/x_permitted_cross_domain_policies.rb +19 -34
  15. data/lib/secure_headers/headers/x_xss_protection.rb +17 -48
  16. data/lib/secure_headers/middleware.rb +15 -0
  17. data/lib/secure_headers/padrino.rb +1 -2
  18. data/lib/secure_headers/railtie.rb +9 -6
  19. data/lib/secure_headers/view_helper.rb +27 -43
  20. data/lib/secure_headers.rb +254 -61
  21. data/secure_headers.gemspec +7 -12
  22. data/spec/lib/secure_headers/configuration_spec.rb +80 -0
  23. data/spec/lib/secure_headers/headers/content_security_policy_spec.rb +111 -276
  24. data/spec/lib/secure_headers/headers/public_key_pins_spec.rb +17 -17
  25. data/spec/lib/secure_headers/headers/strict_transport_security_spec.rb +11 -43
  26. data/spec/lib/secure_headers/headers/x_content_type_options_spec.rb +11 -18
  27. data/spec/lib/secure_headers/headers/x_download_options_spec.rb +13 -17
  28. data/spec/lib/secure_headers/headers/x_frame_options_spec.rb +15 -17
  29. data/spec/lib/secure_headers/headers/x_permitted_cross_domain_policies_spec.rb +22 -39
  30. data/spec/lib/secure_headers/headers/x_xss_protection_spec.rb +20 -30
  31. data/spec/lib/secure_headers/middleware_spec.rb +40 -0
  32. data/spec/lib/secure_headers_spec.rb +201 -339
  33. data/spec/spec_helper.rb +30 -30
  34. data/upgrading-to-3-0.md +35 -0
  35. metadata +14 -100
  36. data/fixtures/rails_3_2_22/.rspec +0 -1
  37. data/fixtures/rails_3_2_22/Gemfile +0 -6
  38. data/fixtures/rails_3_2_22/README.rdoc +0 -261
  39. data/fixtures/rails_3_2_22/Rakefile +0 -7
  40. data/fixtures/rails_3_2_22/app/controllers/application_controller.rb +0 -4
  41. data/fixtures/rails_3_2_22/app/controllers/other_things_controller.rb +0 -5
  42. data/fixtures/rails_3_2_22/app/controllers/things_controller.rb +0 -5
  43. data/fixtures/rails_3_2_22/app/models/.gitkeep +0 -0
  44. data/fixtures/rails_3_2_22/app/views/layouts/application.html.erb +0 -11
  45. data/fixtures/rails_3_2_22/app/views/other_things/index.html.erb +0 -2
  46. data/fixtures/rails_3_2_22/app/views/things/index.html.erb +0 -1
  47. data/fixtures/rails_3_2_22/config/application.rb +0 -14
  48. data/fixtures/rails_3_2_22/config/boot.rb +0 -6
  49. data/fixtures/rails_3_2_22/config/environment.rb +0 -5
  50. data/fixtures/rails_3_2_22/config/environments/test.rb +0 -37
  51. data/fixtures/rails_3_2_22/config/initializers/secure_headers.rb +0 -16
  52. data/fixtures/rails_3_2_22/config/routes.rb +0 -4
  53. data/fixtures/rails_3_2_22/config/script_hashes.yml +0 -5
  54. data/fixtures/rails_3_2_22/config.ru +0 -7
  55. data/fixtures/rails_3_2_22/lib/assets/.gitkeep +0 -0
  56. data/fixtures/rails_3_2_22/lib/tasks/.gitkeep +0 -0
  57. data/fixtures/rails_3_2_22/log/.gitkeep +0 -0
  58. data/fixtures/rails_3_2_22/spec/controllers/other_things_controller_spec.rb +0 -83
  59. data/fixtures/rails_3_2_22/spec/controllers/things_controller_spec.rb +0 -54
  60. data/fixtures/rails_3_2_22/spec/spec_helper.rb +0 -15
  61. data/fixtures/rails_3_2_22/vendor/assets/javascripts/.gitkeep +0 -0
  62. data/fixtures/rails_3_2_22/vendor/assets/stylesheets/.gitkeep +0 -0
  63. data/fixtures/rails_3_2_22/vendor/plugins/.gitkeep +0 -0
  64. data/fixtures/rails_3_2_22_no_init/.rspec +0 -1
  65. data/fixtures/rails_3_2_22_no_init/Gemfile +0 -6
  66. data/fixtures/rails_3_2_22_no_init/README.rdoc +0 -261
  67. data/fixtures/rails_3_2_22_no_init/Rakefile +0 -7
  68. data/fixtures/rails_3_2_22_no_init/app/controllers/application_controller.rb +0 -4
  69. data/fixtures/rails_3_2_22_no_init/app/controllers/other_things_controller.rb +0 -20
  70. data/fixtures/rails_3_2_22_no_init/app/controllers/things_controller.rb +0 -5
  71. data/fixtures/rails_3_2_22_no_init/app/models/.gitkeep +0 -0
  72. data/fixtures/rails_3_2_22_no_init/app/views/layouts/application.html.erb +0 -12
  73. data/fixtures/rails_3_2_22_no_init/app/views/other_things/index.html.erb +0 -1
  74. data/fixtures/rails_3_2_22_no_init/app/views/things/index.html.erb +0 -0
  75. data/fixtures/rails_3_2_22_no_init/config/application.rb +0 -17
  76. data/fixtures/rails_3_2_22_no_init/config/boot.rb +0 -6
  77. data/fixtures/rails_3_2_22_no_init/config/environment.rb +0 -5
  78. data/fixtures/rails_3_2_22_no_init/config/environments/test.rb +0 -37
  79. data/fixtures/rails_3_2_22_no_init/config/routes.rb +0 -4
  80. data/fixtures/rails_3_2_22_no_init/config.ru +0 -4
  81. data/fixtures/rails_3_2_22_no_init/lib/assets/.gitkeep +0 -0
  82. data/fixtures/rails_3_2_22_no_init/lib/tasks/.gitkeep +0 -0
  83. data/fixtures/rails_3_2_22_no_init/log/.gitkeep +0 -0
  84. data/fixtures/rails_3_2_22_no_init/spec/controllers/other_things_controller_spec.rb +0 -56
  85. data/fixtures/rails_3_2_22_no_init/spec/controllers/things_controller_spec.rb +0 -54
  86. data/fixtures/rails_3_2_22_no_init/spec/spec_helper.rb +0 -5
  87. data/fixtures/rails_3_2_22_no_init/vendor/assets/javascripts/.gitkeep +0 -0
  88. data/fixtures/rails_3_2_22_no_init/vendor/assets/stylesheets/.gitkeep +0 -0
  89. data/fixtures/rails_3_2_22_no_init/vendor/plugins/.gitkeep +0 -0
  90. data/fixtures/rails_4_1_8/Gemfile +0 -5
  91. data/fixtures/rails_4_1_8/README.rdoc +0 -28
  92. data/fixtures/rails_4_1_8/Rakefile +0 -6
  93. data/fixtures/rails_4_1_8/app/controllers/application_controller.rb +0 -4
  94. data/fixtures/rails_4_1_8/app/controllers/concerns/.keep +0 -0
  95. data/fixtures/rails_4_1_8/app/controllers/other_things_controller.rb +0 -5
  96. data/fixtures/rails_4_1_8/app/controllers/things_controller.rb +0 -5
  97. data/fixtures/rails_4_1_8/app/models/.keep +0 -0
  98. data/fixtures/rails_4_1_8/app/models/concerns/.keep +0 -0
  99. data/fixtures/rails_4_1_8/app/views/layouts/application.html.erb +0 -11
  100. data/fixtures/rails_4_1_8/app/views/other_things/index.html.erb +0 -2
  101. data/fixtures/rails_4_1_8/app/views/things/index.html.erb +0 -1
  102. data/fixtures/rails_4_1_8/config/application.rb +0 -15
  103. data/fixtures/rails_4_1_8/config/boot.rb +0 -4
  104. data/fixtures/rails_4_1_8/config/environment.rb +0 -5
  105. data/fixtures/rails_4_1_8/config/environments/test.rb +0 -10
  106. data/fixtures/rails_4_1_8/config/initializers/secure_headers.rb +0 -16
  107. data/fixtures/rails_4_1_8/config/routes.rb +0 -4
  108. data/fixtures/rails_4_1_8/config/script_hashes.yml +0 -5
  109. data/fixtures/rails_4_1_8/config/secrets.yml +0 -22
  110. data/fixtures/rails_4_1_8/config.ru +0 -4
  111. data/fixtures/rails_4_1_8/lib/assets/.keep +0 -0
  112. data/fixtures/rails_4_1_8/lib/tasks/.keep +0 -0
  113. data/fixtures/rails_4_1_8/log/.keep +0 -0
  114. data/fixtures/rails_4_1_8/spec/controllers/other_things_controller_spec.rb +0 -83
  115. data/fixtures/rails_4_1_8/spec/controllers/things_controller_spec.rb +0 -59
  116. data/fixtures/rails_4_1_8/spec/spec_helper.rb +0 -15
  117. data/fixtures/rails_4_1_8/vendor/assets/javascripts/.keep +0 -0
  118. data/fixtures/rails_4_1_8/vendor/assets/stylesheets/.keep +0 -0
  119. data/lib/secure_headers/controller_extension.rb +0 -158
  120. data/lib/secure_headers/hash_helper.rb +0 -7
  121. data/lib/secure_headers/header.rb +0 -5
  122. data/lib/secure_headers/headers/content_security_policy/script_hash_middleware.rb +0 -22
  123. data/lib/secure_headers/version.rb +0 -3
  124. data/lib/tasks/tasks.rake +0 -48
  125. data/spec/lib/secure_headers/headers/content_security_policy/script_hash_middleware_spec.rb +0 -46
data/README.md CHANGED
@@ -1,5 +1,9 @@
1
1
  # SecureHeaders [![Build Status](https://travis-ci.org/twitter/secureheaders.png?branch=master)](http://travis-ci.org/twitter/secureheaders) [![Code Climate](https://codeclimate.com/github/twitter/secureheaders.png)](https://codeclimate.com/github/twitter/secureheaders) [![Coverage Status](https://coveralls.io/repos/twitter/secureheaders/badge.png)](https://coveralls.io/r/twitter/secureheaders)
2
2
 
3
+ **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.
4
+
5
+ **The [2.x branch](https://github.com/twitter/secureheaders/tree/2.x) will be maintained**. The documentation below only applies to the 2.x branch. See the 2.x [README](https://github.com/twitter/secureheaders/blob/2.x/README.md) for the old way of doing things.
6
+
3
7
  The gem will automatically apply several headers that are related to security. This includes:
4
8
  - Content Security Policy (CSP) - Helps detect/prevent XSS, mixed-content, and other classes of attack. [CSP 2 Specification](http://www.w3.org/TR/CSP2/)
5
9
  - 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)
@@ -8,245 +12,188 @@ The gem will automatically apply several headers that are related to security.
8
12
  - X-Content-Type-Options - [Prevent content type sniffing](http://msdn.microsoft.com/en-us/library/ie/gg622941\(v=vs.85\).aspx)
9
13
  - X-Download-Options - [Prevent file downloads opening](http://msdn.microsoft.com/en-us/library/ie/jj542450(v=vs.85).aspx)
10
14
  - 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)
11
- - 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)
12
-
13
- ## Usage
14
-
15
- - `ensure_security_headers` in a controller will set security-related headers automatically based on the configuration below.
15
+ - 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)
16
16
 
17
- ### Disabling
18
-
19
- Use the standard `skip_before_filter :filter_name, options` mechanism. e.g. `skip_before_filter :set_csp_header, :only => :tinymce_page`
20
-
21
- The following methods are going to be called, unless they are provided in a `skip_before_filter` block.
22
-
23
- * `:set_csp_header`
24
- * `:set_hsts_header`
25
- * `:set_hpkp_header`
26
- * `:set_x_frame_options_header`
27
- * `:set_x_xss_protection_header`
28
- * `:set_x_content_type_options_header`
29
- * `:set_x_download_options_header`
30
- * `:set_x_permitted_cross_domain_policies_header`
17
+ `secure_headers` is a library with a global config, per request overrides, and rack milddleware that enables you customize your application settings.
31
18
 
32
19
  ## Configuration
33
20
 
34
- **Place the following in an initializer (recommended):**
21
+ 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.
35
22
 
36
- **NOTE: All CSP config values accept procs for one way of dynamically setting values**
23
+ All `nil` values will fallback to their default value. `SecureHeaders::OPT_OUT` will disable the header entirely.
37
24
 
38
25
  ```ruby
39
- ::SecureHeaders::Configuration.configure do |config|
40
- config.hsts = {:max_age => 20.years.to_i, :include_subdomains => true}
41
- config.x_frame_options = 'DENY'
26
+ SecureHeaders::Configuration.default do |config|
27
+ config.hsts = 20.years.to_i.to_s
28
+ config.x_frame_options = "DENY"
42
29
  config.x_content_type_options = "nosniff"
43
- config.x_xss_protection = {:value => 1, :mode => 'block'}
44
- config.x_download_options = 'noopen'
45
- config.x_permitted_cross_domain_policies = 'none'
30
+ config.x_xss_protection = "1; mode=block"
31
+ config.x_download_options = "noopen"
32
+ config.x_permitted_cross_domain_policies = "none"
46
33
  config.csp = {
47
- :default_src => "https: 'self'",
48
- :enforce => proc {|controller| controller.my_feature_flag_api.enabled? },
49
- :frame_src => "https: http:.twimg.com http://itunes.apple.com",
50
- :img_src => "https:",
51
- :connect_src => "wws:",
52
- :font_src => "'self' data:",
53
- :frame_src => "'self'",
54
- :img_src => "mycdn.com data:",
55
- :media_src => "utoob.com",
56
- :object_src => "'self'",
57
- :script_src => "'self'",
58
- :style_src => "'unsafe-inline'",
59
- :base_uri => "'self'",
60
- :child_src => "'self'",
61
- :form_action => "'self' github.com",
62
- :frame_ancestors => "'none'",
63
- :plugin_types => 'application/x-shockwave-flash',
64
- :block_all_mixed_content => '', # see [http://www.w3.org/TR/mixed-content/]()
65
- :report_uri => '//example.com/uri-directive'
34
+ default_src: %w(https: 'self'),
35
+ report_only: false,
36
+ frame_src: %w(*.twimg.com itunes.apple.com),
37
+ connect_src: %w(wws:),
38
+ font_src: %w('self' data:),
39
+ frame_src: %w('self'),
40
+ img_src: %w(mycdn.com data:),
41
+ media_src: %w(utoob.com),
42
+ object_src: %w('self'),
43
+ script_src: %w('self'),
44
+ style_src: %w('unsafe-inline'),
45
+ base_uri: %w('self'),
46
+ child_src: %w('self'),
47
+ form_action: %w('self' github.com),
48
+ frame_ancestors: %w('none'),
49
+ plugin_types: %w(application/x-shockwave-flash),
50
+ block_all_mixed_content: true, # see [http://www.w3.org/TR/mixed-content/](http://www.w3.org/TR/mixed-content/)
51
+ report_uri: %w(https://example.com/uri-directive)
66
52
  }
67
53
  config.hpkp = {
68
- :max_age => 60.days.to_i,
69
- :include_subdomains => true,
70
- :report_uri => '//example.com/uri-directive',
71
- :pins => [
72
- {:sha256 => 'abc'},
73
- {:sha256 => '123'}
54
+ report_only: false,
55
+ max_age: 60.days.to_i,
56
+ include_subdomains: true,
57
+ report_uri: "https://example.com/uri-directive",
58
+ pins: [
59
+ {sha256: "abc"},
60
+ {sha256: "123"}
74
61
  ]
75
62
  }
76
63
  end
77
-
78
- # and then include this in application_controller.rb
79
- class ApplicationController < ActionController::Base
80
- ensure_security_headers
81
- end
82
64
  ```
83
65
 
84
- Or do the config as a parameter to `ensure_security_headers`
66
+ ### rails 2
67
+
68
+ 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.
85
69
 
86
70
  ```ruby
87
- ensure_security_headers(
88
- :hsts => {:include_subdomains => true, :max_age => 20.years.to_i},
89
- :x_frame_options => 'DENY',
90
- :csp => false
91
- )
71
+ use SecureHeaders::Middleware
92
72
  ```
93
73
 
94
- ## Per-action configuration
74
+ ## Default values
75
+
76
+ 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).
77
+
78
+ ## Named overrides
79
+
80
+ Named overrides serve two purposes:
95
81
 
96
- Sometimes you need to override your content security policy for a given endpoint. Rather than applying the exception globally, you have a few options:
82
+ * To be able to refer to a configuration by simple name.
83
+ * By precomputing the headers for a named configuration, the headers generated once and reused over every request.
97
84
 
98
- 1. Use procs as config values as mentioned above.
99
- 1. Specifying `ensure_security_headers csp: ::SecureHeaders::Configuration.csp.merge(script_src: shadyhost.com)` in a descendent controller will override the settings for that controller only.
100
- 1. Override the `secure_header_options_for` class instance method. e.g.
85
+ 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.
101
86
 
102
87
  ```ruby
103
- class SomethingController < ApplicationController
104
- def wumbus
105
- # gets style-src override
88
+ class ApplicationController < ActionController::Base
89
+ SecureHeaders::Configuration.default do |config|
90
+ config.csp = {
91
+ default_src: %w('self'),
92
+ script_src: %w(example.org)
93
+ }
106
94
  end
107
95
 
108
- def diffendoofer
109
- # does not get style-src override
96
+ # override default configuration
97
+ SecureHeaders::Configuration.override(:script_from_otherdomain_com) do |config|
98
+ config.csp[:script_src] << "otherdomain.com"
110
99
  end
111
100
 
112
- def secure_header_options_for(header, options)
113
- options = super
114
- if params[:action] == "wumbus"
115
- if header == :csp
116
- options.merge(style_src: "'self'")
117
- end
118
- else
119
- options
120
- end
101
+ # overrides the :script_from_otherdomain_com configuration
102
+ SecureHeaders::Configuration.override(:another_config, :script_from_otherdomain_com) do |config|
103
+ config.csp[:script_src] << "evenanotherdomain.com"
121
104
  end
122
105
  end
123
- ```
124
-
125
- ## Options for ensure\_security\_headers
126
-
127
- **To disable any of these headers, supply a value of false (e.g. :hsts => false), supplying nil will set the default value**
128
-
129
- Each header configuration can take a hash, or a string, or both. If a string
130
- is provided, that value is inserted verbatim. If a hash is supplied, a
131
- header will be constructed using the supplied options.
132
-
133
- ### The Easy Headers
134
106
 
135
- This configuration will likely work for most applications without modification.
107
+ class MyController < ApplicationController
108
+ def index
109
+ # Produces default-src 'self'; script-src example.org otherdomain.org
110
+ use_secure_headers_override(:script_from_otherdomain_com)
111
+ end
136
112
 
137
- ```ruby
138
- :hsts => {:max_age => 631138519, :include_subdomains => false}
139
- :x_frame_options => {:value => 'SAMEORIGIN'}
140
- :x_xss_protection => {:value => 1, :mode => 'block'} # set the :mode option to false to use "warning only" mode
141
- :x_content_type_options => {:value => 'nosniff'}
142
- :x_download_options => {:value => 'noopen'}
143
- :x_permitted_cross_domain_policies => {:value => 'none'}
113
+ def show
114
+ # Produces default-src 'self'; script-src example.org otherdomain.org evenanotherdomain.com
115
+ use_secure_headers_override(:another_config)
116
+ end
117
+ end
144
118
  ```
145
119
 
146
- ### Content Security Policy (CSP)
120
+ By default, a noop configuration is provided. No headers will be set when this default override is used.
147
121
 
148
122
  ```ruby
149
- :csp => {
150
- :enforce => false, # sets header to report-only, by default
151
- # default_src is required!
152
- :default_src => nil, # sets the default-src/allow+options directives
153
-
154
- # Where reports are sent. Use protocol relative URLs if you are posting to the same domain (TLD+1). Use paths if you are posting to the application serving the header
155
- :report_uri => '//mysite.example.com',
156
-
157
- # these directives all take 'none', 'self', or a globbed pattern
158
- :img_src => nil,
159
- :frame_src => nil,
160
- :connect_src => nil,
161
- :font_src => nil,
162
- :media_src => nil,
163
- :object_src => nil,
164
- :style_src => nil,
165
- :script_src => nil,
166
-
167
- # http additions will be appended to the various directives when
168
- # over http, relaxing the policy
169
- # e.g.
170
- # :csp => {
171
- # :img_src => 'https:',
172
- # :http_additions => {:img_src => 'http'}
173
- # }
174
- # would produce the directive: "img-src https: http:;"
175
- # when over http, ignored for https requests
176
- :http_additions => {}
177
- }
123
+ class MyController < ApplicationController
124
+ def index
125
+ SecureHeaders::opt_out_of_all_protection(request)
126
+ end
127
+ end
178
128
  ```
179
129
 
180
- ### Example CSP header config
130
+ ## Per-action configuration
181
131
 
132
+ You can override the settings for a given action by producing a temporary override. This approach is not recommended because the header values will be computed per request.
182
133
 
183
134
  ```ruby
184
- # most basic example
185
- :csp => {
186
- :default_src => "https: 'unsafe-inline' 'unsafe-eval'",
187
- :report_uri => '/uri-directive'
188
- }
189
-
190
- > "default-src 'unsafe-inline' 'unsafe-eval' https:; report-uri /uri-directive;"
135
+ # Given a config of:
136
+ ::SecureHeaders::Configuration.default do |config|
137
+ config.csp = {
138
+ default_src: %w('self'),
139
+ script_src: %w('self')
140
+ }
141
+ end
191
142
 
192
- # turn off inline scripting/eval
193
- :csp => {
194
- :default_src => 'https:',
195
- :report_uri => '/uri-directive'
196
- }
143
+ class MyController < ApplicationController
144
+ def index
145
+ # Append value to the source list, override 'none' values
146
+ # Produces: default-src 'self'; script-src 'self' s3.amazaonaws.com; object-src 'self' youtube.com
147
+ append_content_security_policy_directives(script_src: %w(s3.amazaonaws.com), object_src: %w('self' youtube.com))
197
148
 
198
- > "default-src https:; report-uri /uri-directive;"
149
+ # Overrides the previously set source list, override 'none' values
150
+ # Produces: default-src 'self'; script-src s3.amazaonaws.com; object-src 'self'
151
+ override_content_security_policy_directive(script_src: %w(s3.amazaonaws.com), object_src: %w('self'))
199
152
 
200
- # Auction site wants to allow images from anywhere, plugin content from a list of trusted media providers (including a content distribution network), and scripts only from its server hosting sanitized JavaScript
201
- :csp => {
202
- :default_src => "'self'",
203
- :img_src => '*',
204
- :object_src => ['media1.com', 'media2.com', '*.cdn.com'],
205
- # alternatively (NOT csv) :object_src => 'media1.com media2.com *.cdn.com'
206
- :script_src => 'trustedscripts.example.com'
207
- }
208
- "default-src 'self'; img-src *; object-src media1.com media2.com *.cdn.com; script-src trustedscripts.example.com;"
153
+ # Global settings default to "sameorigin"
154
+ override_x_frame_options("DENY")
155
+ end
209
156
  ```
210
157
 
211
- ### Tagging Requests
158
+ 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.
159
+ * `append_content_security_policy_directives(hash)`: appends each value to the corresponding CSP app-wide configuration.
160
+ * `override_content_security_policy_directive(hash)`: merges the hash into the app-wide configuration, overwriting any previous config
161
+ * `override_x_frame_options(value)`: sets the `X-Frame-Options header` to `value`
212
162
 
213
- It's often valuable to send extra information in the report uri that is not available in the reports themselves. Namely, "was the policy enforced" and "where did the report come from"
163
+ ## Appending / overriding Content Security Policy
214
164
 
215
- ```ruby
216
- {
217
- :tag_report_uri => true,
218
- :enforce => true,
219
- :app_name => 'twitter',
220
- :report_uri => 'csp_reports'
221
- }
222
- ```
165
+ 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:)}`.
223
166
 
224
- Results in
225
- ```
226
- report-uri csp_reports?enforce=true&app_name=twitter
227
- ```
167
+ #### Append to the policy with a directive other than `default_src`
168
+
169
+ The value of `default_src` is joined with the addition. 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:
170
+
171
+ ```ruby
172
+ ::SecureHeaders::Configuration.configure do |config|
173
+ config.csp = {
174
+ default_src: %w('self')
175
+ }
176
+ end
177
+ ```
228
178
 
229
- ### CSP Level 2 features
179
+ Code | Result
180
+ ------------- | -------------
181
+ `append_content_security_policy_directives(script_src: %w(mycdn.com))` | `default-src 'self'; script-src 'self' mycdn.com`
182
+ `override_content_security_policy_directives(script_src: %w(mycdn.com))` | `default-src 'self'; script-src mycdn.com`
230
183
 
231
- *NOTE: Currently, only erb is supported. Mustache support isn't far off. Hash sources are valid for inline style blocks but are not yet supported by secure_headers.*
184
+ Code | Result
185
+ ------------- | -------------
186
+ `append_content_security_policy_directives(script_src: %w(mycdn.com))` | `default-src https:; script-src https: mycdn.com`
187
+ `override_content_security_policy_directives(script_src: %w(mycdn.com))` | `default-src https:; script-src mycdn.com`
232
188
 
233
189
  #### Nonce
234
190
 
235
- script/style-nonce can be used to whitelist inline content. To do this, add "nonce" to your script/style-src configuration, then set the nonce attributes on the various tags.
191
+ script/style-nonce can be used to whitelist inline content. To do this, call the SecureHeaders::content_security_policy_nonce then set the nonce attributes on the various tags.
236
192
 
237
193
  Setting a nonce will also set 'unsafe-inline' for browsers that don't support nonces for backwards compatibility. 'unsafe-inline' is ignored if a nonce is present in a directive in compliant browsers.
238
194
 
239
- ```ruby
240
- :csp => {
241
- :default_src => "'self'",
242
- :script_src => "'self' nonce"
243
- }
244
- ```
245
-
246
- > content-security-policy: default-src 'self'; script-src 'self' 'nonce-abc123' 'unsafe-inline'
247
-
248
195
  ```erb
249
- <script nonce="<%= @content_security_policy_nonce %>">
196
+ <script nonce="<%= content_security_policy_nonce %>">
250
197
  console.log("whitelisted, will execute")
251
198
  </script>
252
199
 
@@ -258,12 +205,19 @@ Setting a nonce will also set 'unsafe-inline' for browsers that don't support no
258
205
  console.log("won't execute, not whitelisted")
259
206
  </script>
260
207
  ```
208
+
261
209
  You can use a view helper to automatically add nonces to script tags:
210
+
262
211
  ```erb
263
212
  <%= nonced_javascript_tag do %>
264
- console.log("nonced!")
213
+ console.log("hai");
214
+ <% end %>
215
+
216
+ <%= nonced_style_tag do %>
217
+ body {
218
+ background-color: black;
219
+ }
265
220
  <% end %>
266
- <%= nonced_javascript_tag("nonced without a block!") %>
267
221
  ```
268
222
 
269
223
  becomes:
@@ -272,90 +226,22 @@ becomes:
272
226
  <script nonce="/jRAxuLJsDXAxqhNBB7gg7h55KETtDQBXe4ZL+xIXwI=">
273
227
  console.log("nonced!")
274
228
  </script>
229
+ <style nonce="/jRAxuLJsDXAxqhNBB7gg7h55KETtDQBXe4ZL+xIXwI=">
230
+ body {
231
+ background-color: black;
232
+ }
233
+ </style>
275
234
  ```
276
235
 
277
236
  #### Hash
278
237
 
279
- setting hash source values will also set 'unsafe-inline' for browsers that don't support hash sources for backwards compatibility. 'unsafe-inline' is ignored if a hash is present in a directive in compliant browsers.
280
-
281
- Hash source support works by taking the hash value of the contents of an inline script block and adding the hash "fingerprint" to the CSP header.
282
-
283
- If you only have a few hashes, you can hardcode them for the entire app:
284
-
285
- ```ruby
286
- config.csp = {
287
- :default_src => "https:",
288
- :script_src => "'self'"
289
- :script_hashes => ['sha1-abc', 'sha1-qwe']
290
- }
291
- ```
292
-
293
- The following will work as well, but may not be as clear:
294
-
295
- ```ruby
296
- config.csp = {
297
- :default_src => "https:",
298
- :script_src => "'self' 'sha1-qwe'"
299
- }
300
- ```
301
-
302
- If you find you have many hashes or the content of the script tags change frequently, you can apply these hashes in a more intelligent way. This method expects config/script_hashes.yml to contain a map of templates => [hashes]. When the individual templates, layouts, or partials are rendered the hash values for the script tags in those templates will be automatically added to the header. *Currently, only erb layouts are supported.* This requires the use of middleware:
303
-
304
- ```ruby
305
- # config.ru
306
- require 'secure_headers/headers/content_security_policy/script_hash_middleware'
307
- use ::SecureHeaders::ContentSecurityPolicy::ScriptHashMiddleware
308
- ```
309
-
310
- ```ruby
311
- config.csp = {
312
- :default_src => "https:",
313
- :script_src => "'self'",
314
- :script_hash_middleware => true
315
- }
316
- ```
317
-
318
- Hashes are stored in a yaml file with a mapping of Filename => [list of hashes] in config/script_hashes.yml. You can automatically populate this file by running the following rake task:
319
-
320
- ```$ bundle exec rake secure_headers:generate_hashes```
321
-
322
- Which will generate something like:
323
-
324
- ```yaml
325
- # config/script_hashes.yml
326
- app/views/layouts/application.html.erb:
327
- - sha256-l8OLjZqYRnKilpdE0VosRMvhdYArjXT4NZaK2p7QVvs=
328
- app/templates/articles/edit.html.erb:
329
- - sha256-+7mij1/uCwtCQRWrof2NmOln5qX+5WdVwTLMpi8nuoA=
330
- - sha256-Ny4TRIhhFpnYnSeKC274P6bfAz4TOkezLabavIAU4dA=
331
- - sha256-I5e58Gqbu4WpO9dck18QxO7aYOHKrELIi70it4jIPi0=
332
- - sha256-Po4LMynwnAJHxiTp3DQaQ3YDBj3paN/xrDoKl4OyxY4=
333
- ```
334
-
335
- In this example, if we visit /articles/edit/[id], the above hashes will automatically be added to the CSP header's
336
- script-src value!
337
-
338
- You can use plain "script" tags or you can use a built-in helper:
339
-
340
- ```erb
341
- <%= hashed_javascript_tag do %>
342
- console.log("hashed automatically!")
343
- <% end %>
344
- ```
345
-
346
- By using the helper, hash values will be computed dynamically in development/test environments. If a dynamically computed hash value does not match what is expected to be found in config/script_hashes.yml a warning message will be printed to the console. If you want to raise exceptions instead, use:
347
-
348
- ```erb
349
- <%= hashed_javascript_tag(raise_error_on_unrecognized_hash = true) do %>
350
- console.log("will raise an exception if not in script_hashes.yml!")
351
- <% end %>
352
- ```
238
+ The hash feature has been removed, for now.
353
239
 
354
240
  ### Public Key Pins
355
241
 
356
242
  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.
357
243
 
358
- ```
244
+ ```ruby
359
245
  config.hpkp = {
360
246
  max_age: 60.days.to_i, # max_age is a required parameter
361
247
  include_subdomains: true, # whether or not to apply pins to subdomains
@@ -364,7 +250,7 @@ config.hpkp = {
364
250
  {sha256: 'b5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b878ae4944c'},
365
251
  {sha256: '73a2c64f9545172c1195efb6616ca5f7afd1df6f245407cafb90de3998a1c97f'}
366
252
  ],
367
- enforce: true, # defaults to false (report-only mode)
253
+ report_only: true, # defaults to false (report-only mode)
368
254
  report_uri: '//example.com/uri-directive',
369
255
  app_name: 'example',
370
256
  tag_report_uri: true
@@ -381,28 +267,17 @@ require 'sinatra'
381
267
  require 'haml'
382
268
  require 'secure_headers'
383
269
 
384
- ::SecureHeaders::Configuration.configure do |config|
385
- config.hsts = {:max_age => 99, :include_subdomains => true}
386
- config.x_frame_options = 'DENY'
387
- config.x_content_type_options = "nosniff"
388
- config.x_xss_protection = {:value => 1, :mode => false}
389
- config.x_download_options = 'noopen'
390
- config.x_permitted_cross_domain_policies = 'none'
391
- config.csp = {
392
- :default_src => "https: inline eval",
393
- :report_uri => '//example.com/uri-directive',
394
- :img_src => "https: data:",
395
- :frame_src => "https: http:.twimg.com http://itunes.apple.com"
396
- }
397
- config.hpkp = false
270
+ use SecureHeaders::Middleware
271
+
272
+ SecureHeaders::Configuration.configure do |config|
273
+ ...
398
274
  end
399
275
 
400
276
  class Donkey < Sinatra::Application
401
- include SecureHeaders
402
277
  set :root, APP_ROOT
403
278
 
404
279
  get '/' do
405
- set_csp_header
280
+ SecureHeaders.override_x_frame_options(SecureHeaders::OPT_OUT)
406
281
  haml :index
407
282
  end
408
283
  end
@@ -415,12 +290,13 @@ You can use SecureHeaders for Padrino applications as well:
415
290
  In your `Gemfile`:
416
291
 
417
292
  ```ruby
418
- gem "secure_headers", :require => 'secure_headers'
293
+ gem "secure_headers", require: 'secure_headers'
419
294
  ```
420
295
 
421
296
  then in your `app.rb` file you can:
422
297
 
423
298
  ```ruby
299
+ Padrino.use(SecureHeaders::Middleware)
424
300
  require 'secure_headers/padrino'
425
301
 
426
302
  module Web
@@ -428,7 +304,6 @@ module Web
428
304
  register SecureHeaders::Padrino
429
305
 
430
306
  get '/' do
431
- set_csp_header
432
307
  render 'index'
433
308
  end
434
309
  end
@@ -439,48 +314,8 @@ and in `config/boot.rb`:
439
314
 
440
315
  ```ruby
441
316
  def before_load
442
- ::SecureHeaders::Configuration.configure do |config|
443
- config.hsts = {:max_age => 99, :include_subdomains => true}
444
- config.x_frame_options = 'DENY'
445
- config.x_content_type_options = "nosniff"
446
- config.x_xss_protection = {:value => '1', :mode => false}
447
- config.x_download_options = 'noopen'
448
- config.x_permitted_cross_domain_policies = 'none'
449
- config.csp = {
450
- :default_src => "https: inline eval",
451
- :report_uri => '//example.com/uri-directive',
452
- :img_src => "https: data:",
453
- :frame_src => "https: http:.twimg.com http://itunes.apple.com"
454
- }
455
- end
456
- end
457
- ```
458
-
459
- ### Using in rack middleware
460
-
461
- The `SecureHeaders::header_hash` generates a hash of all header values, which is useful for merging with rack middleware values.
462
-
463
- ```ruby
464
- class MySecureHeaders
465
- include SecureHeaders
466
- def initialize(app)
467
- @app = app
468
- end
469
-
470
- def call(env)
471
- status, headers, response = @app.call(env)
472
- security_headers = if override?
473
- SecureHeaders::header_hash(:csp => false) # uses global config, but overrides CSP config
474
- else
475
- SecureHeaders::header_hash # uses global config
476
- end
477
- [status, headers.merge(security_headers), [response.body]]
478
- end
479
- end
480
-
481
- module Testapp
482
- class Application < Rails::Application
483
- config.middleware.use MySecureHeaders
317
+ SecureHeaders::Configuration.configure do |config|
318
+ ...
484
319
  end
485
320
  end
486
321
  ```
@@ -495,18 +330,6 @@ end
495
330
  * Python - [django-csp](https://github.com/mozilla/django-csp) + [commonware](https://github.com/jsocol/commonware/); [django-security](https://github.com/sdelements/django-security)
496
331
  * Go - [secureheader](https://github.com/kr/secureheader)
497
332
 
498
- ## Authors
499
-
500
- * Neil Matatall [@ndm](https://twitter.com/ndm) - primary author.
501
- * Nicholas Green [@nickgreen](https://twitter.com/nickgreen) - code contributions, main reviewer.
502
-
503
- ## Acknowledgements
504
-
505
- * Justin Collins [@presidentbeef](https://twitter.com/presidentbeef) & Jim O'Leary [@jimio](https://twitter.com/jimio) for reviews.
506
- * Ian Melven [@imelven](https://twitter.com/imelven) - Discussions/info about CSP in general, made us aware of the [userCSP](https://addons.mozilla.org/en-US/firefox/addon/newusercspdesign/) Mozilla extension.
507
- * Sumit Shah [@omnidactyl](https://twitter.com/omnidactyl) - For being an eager guinea pig.
508
- * Chris Aniszczyk [@cra](https://twitter.com/cra) - For running an awesome open source program at Twitter.
509
-
510
333
  ## License
511
334
 
512
335
  Copyright 2013-2014 Twitter, Inc and other contributors.