secure_headers 2.2.4 → 3.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 (127) hide show
  1. checksums.yaml +4 -4
  2. data/.rspec +1 -0
  3. data/.ruby-version +1 -1
  4. data/.travis.yml +3 -0
  5. data/CHANGELOG.md +205 -0
  6. data/Gemfile +9 -10
  7. data/Guardfile +12 -0
  8. data/README.md +172 -274
  9. data/Rakefile +2 -36
  10. data/lib/secure_headers/configuration.rb +189 -0
  11. data/lib/secure_headers/headers/content_security_policy.rb +362 -211
  12. data/lib/secure_headers/headers/public_key_pins.rb +43 -57
  13. data/lib/secure_headers/headers/strict_transport_security.rb +21 -47
  14. data/lib/secure_headers/headers/x_content_type_options.rb +19 -32
  15. data/lib/secure_headers/headers/x_download_options.rb +18 -31
  16. data/lib/secure_headers/headers/x_frame_options.rb +24 -32
  17. data/lib/secure_headers/headers/x_permitted_cross_domain_policies.rb +19 -32
  18. data/lib/secure_headers/headers/x_xss_protection.rb +17 -46
  19. data/lib/secure_headers/middleware.rb +15 -0
  20. data/lib/secure_headers/padrino.rb +1 -2
  21. data/lib/secure_headers/railtie.rb +9 -6
  22. data/lib/secure_headers/view_helper.rb +27 -44
  23. data/lib/secure_headers.rb +245 -150
  24. data/secure_headers.gemspec +7 -11
  25. data/spec/lib/secure_headers/configuration_spec.rb +80 -0
  26. data/spec/lib/secure_headers/headers/content_security_policy_spec.rb +175 -262
  27. data/spec/lib/secure_headers/headers/public_key_pins_spec.rb +17 -17
  28. data/spec/lib/secure_headers/headers/strict_transport_security_spec.rb +11 -43
  29. data/spec/lib/secure_headers/headers/x_content_type_options_spec.rb +13 -18
  30. data/spec/lib/secure_headers/headers/x_download_options_spec.rb +13 -17
  31. data/spec/lib/secure_headers/headers/x_frame_options_spec.rb +15 -17
  32. data/spec/lib/secure_headers/headers/x_permitted_cross_domain_policies_spec.rb +22 -39
  33. data/spec/lib/secure_headers/headers/x_xss_protection_spec.rb +22 -30
  34. data/spec/lib/secure_headers/middleware_spec.rb +40 -0
  35. data/spec/lib/secure_headers_spec.rb +204 -278
  36. data/spec/spec_helper.rb +32 -28
  37. data/upgrading-to-3-0.md +37 -0
  38. metadata +14 -94
  39. data/fixtures/rails_3_2_12/.rspec +0 -1
  40. data/fixtures/rails_3_2_12/Gemfile +0 -6
  41. data/fixtures/rails_3_2_12/README.rdoc +0 -261
  42. data/fixtures/rails_3_2_12/Rakefile +0 -7
  43. data/fixtures/rails_3_2_12/app/controllers/application_controller.rb +0 -4
  44. data/fixtures/rails_3_2_12/app/controllers/other_things_controller.rb +0 -5
  45. data/fixtures/rails_3_2_12/app/controllers/things_controller.rb +0 -5
  46. data/fixtures/rails_3_2_12/app/models/.gitkeep +0 -0
  47. data/fixtures/rails_3_2_12/app/views/layouts/application.html.erb +0 -11
  48. data/fixtures/rails_3_2_12/app/views/other_things/index.html.erb +0 -2
  49. data/fixtures/rails_3_2_12/app/views/things/index.html.erb +0 -1
  50. data/fixtures/rails_3_2_12/config/application.rb +0 -14
  51. data/fixtures/rails_3_2_12/config/boot.rb +0 -6
  52. data/fixtures/rails_3_2_12/config/environment.rb +0 -5
  53. data/fixtures/rails_3_2_12/config/environments/test.rb +0 -37
  54. data/fixtures/rails_3_2_12/config/initializers/secure_headers.rb +0 -17
  55. data/fixtures/rails_3_2_12/config/routes.rb +0 -4
  56. data/fixtures/rails_3_2_12/config/script_hashes.yml +0 -5
  57. data/fixtures/rails_3_2_12/config.ru +0 -7
  58. data/fixtures/rails_3_2_12/lib/assets/.gitkeep +0 -0
  59. data/fixtures/rails_3_2_12/lib/tasks/.gitkeep +0 -0
  60. data/fixtures/rails_3_2_12/log/.gitkeep +0 -0
  61. data/fixtures/rails_3_2_12/spec/controllers/other_things_controller_spec.rb +0 -83
  62. data/fixtures/rails_3_2_12/spec/controllers/things_controller_spec.rb +0 -54
  63. data/fixtures/rails_3_2_12/spec/spec_helper.rb +0 -15
  64. data/fixtures/rails_3_2_12/vendor/assets/javascripts/.gitkeep +0 -0
  65. data/fixtures/rails_3_2_12/vendor/assets/stylesheets/.gitkeep +0 -0
  66. data/fixtures/rails_3_2_12/vendor/plugins/.gitkeep +0 -0
  67. data/fixtures/rails_3_2_12_no_init/.rspec +0 -1
  68. data/fixtures/rails_3_2_12_no_init/Gemfile +0 -5
  69. data/fixtures/rails_3_2_12_no_init/README.rdoc +0 -261
  70. data/fixtures/rails_3_2_12_no_init/Rakefile +0 -7
  71. data/fixtures/rails_3_2_12_no_init/app/controllers/application_controller.rb +0 -4
  72. data/fixtures/rails_3_2_12_no_init/app/controllers/other_things_controller.rb +0 -20
  73. data/fixtures/rails_3_2_12_no_init/app/controllers/things_controller.rb +0 -5
  74. data/fixtures/rails_3_2_12_no_init/app/models/.gitkeep +0 -0
  75. data/fixtures/rails_3_2_12_no_init/app/views/layouts/application.html.erb +0 -12
  76. data/fixtures/rails_3_2_12_no_init/app/views/other_things/index.html.erb +0 -1
  77. data/fixtures/rails_3_2_12_no_init/app/views/things/index.html.erb +0 -0
  78. data/fixtures/rails_3_2_12_no_init/config/application.rb +0 -17
  79. data/fixtures/rails_3_2_12_no_init/config/boot.rb +0 -6
  80. data/fixtures/rails_3_2_12_no_init/config/environment.rb +0 -5
  81. data/fixtures/rails_3_2_12_no_init/config/environments/test.rb +0 -37
  82. data/fixtures/rails_3_2_12_no_init/config/routes.rb +0 -4
  83. data/fixtures/rails_3_2_12_no_init/config.ru +0 -4
  84. data/fixtures/rails_3_2_12_no_init/lib/assets/.gitkeep +0 -0
  85. data/fixtures/rails_3_2_12_no_init/lib/tasks/.gitkeep +0 -0
  86. data/fixtures/rails_3_2_12_no_init/log/.gitkeep +0 -0
  87. data/fixtures/rails_3_2_12_no_init/spec/controllers/other_things_controller_spec.rb +0 -56
  88. data/fixtures/rails_3_2_12_no_init/spec/controllers/things_controller_spec.rb +0 -54
  89. data/fixtures/rails_3_2_12_no_init/spec/spec_helper.rb +0 -5
  90. data/fixtures/rails_3_2_12_no_init/vendor/assets/javascripts/.gitkeep +0 -0
  91. data/fixtures/rails_3_2_12_no_init/vendor/assets/stylesheets/.gitkeep +0 -0
  92. data/fixtures/rails_3_2_12_no_init/vendor/plugins/.gitkeep +0 -0
  93. data/fixtures/rails_4_1_8/Gemfile +0 -5
  94. data/fixtures/rails_4_1_8/README.rdoc +0 -28
  95. data/fixtures/rails_4_1_8/Rakefile +0 -6
  96. data/fixtures/rails_4_1_8/app/controllers/application_controller.rb +0 -4
  97. data/fixtures/rails_4_1_8/app/controllers/concerns/.keep +0 -0
  98. data/fixtures/rails_4_1_8/app/controllers/other_things_controller.rb +0 -5
  99. data/fixtures/rails_4_1_8/app/controllers/things_controller.rb +0 -5
  100. data/fixtures/rails_4_1_8/app/models/.keep +0 -0
  101. data/fixtures/rails_4_1_8/app/models/concerns/.keep +0 -0
  102. data/fixtures/rails_4_1_8/app/views/layouts/application.html.erb +0 -11
  103. data/fixtures/rails_4_1_8/app/views/other_things/index.html.erb +0 -2
  104. data/fixtures/rails_4_1_8/app/views/things/index.html.erb +0 -1
  105. data/fixtures/rails_4_1_8/config/application.rb +0 -15
  106. data/fixtures/rails_4_1_8/config/boot.rb +0 -4
  107. data/fixtures/rails_4_1_8/config/environment.rb +0 -5
  108. data/fixtures/rails_4_1_8/config/environments/test.rb +0 -10
  109. data/fixtures/rails_4_1_8/config/initializers/secure_headers.rb +0 -17
  110. data/fixtures/rails_4_1_8/config/routes.rb +0 -4
  111. data/fixtures/rails_4_1_8/config/script_hashes.yml +0 -5
  112. data/fixtures/rails_4_1_8/config/secrets.yml +0 -22
  113. data/fixtures/rails_4_1_8/config.ru +0 -4
  114. data/fixtures/rails_4_1_8/lib/assets/.keep +0 -0
  115. data/fixtures/rails_4_1_8/lib/tasks/.keep +0 -0
  116. data/fixtures/rails_4_1_8/log/.keep +0 -0
  117. data/fixtures/rails_4_1_8/spec/controllers/other_things_controller_spec.rb +0 -83
  118. data/fixtures/rails_4_1_8/spec/controllers/things_controller_spec.rb +0 -59
  119. data/fixtures/rails_4_1_8/spec/spec_helper.rb +0 -15
  120. data/fixtures/rails_4_1_8/vendor/assets/javascripts/.keep +0 -0
  121. data/fixtures/rails_4_1_8/vendor/assets/stylesheets/.keep +0 -0
  122. data/lib/secure_headers/hash_helper.rb +0 -7
  123. data/lib/secure_headers/header.rb +0 -5
  124. data/lib/secure_headers/headers/content_security_policy/script_hash_middleware.rb +0 -22
  125. data/lib/secure_headers/version.rb +0 -3
  126. data/lib/tasks/tasks.rake +0 -48
  127. data/spec/lib/secure_headers/headers/content_security_policy/script_hash_middleware_spec.rb +0 -47
data/README.md CHANGED
@@ -1,211 +1,202 @@
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)
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)
2
+
3
+
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
+
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.
2
7
 
3
8
  The gem will automatically apply several headers that are related to security. This includes:
4
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/)
5
10
  - 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)
6
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)
7
- - X-XSS-Protection - [Cross site scripting heuristic filter for IE/Chrome](http://msdn.microsoft.com/en-us/library/dd565647\(v=vs.85\).aspx)
8
- - X-Content-Type-Options - [Prevent content type sniffing](http://msdn.microsoft.com/en-us/library/ie/gg622941\(v=vs.85\).aspx)
9
- - X-Download-Options - [Prevent file downloads opening](http://msdn.microsoft.com/en-us/library/ie/jj542450(v=vs.85).aspx)
12
+ - X-XSS-Protection - [Cross site scripting heuristic filter for IE/Chrome](https://msdn.microsoft.com/en-us/library/dd565647\(v=vs.85\).aspx)
13
+ - X-Content-Type-Options - [Prevent content type sniffing](https://msdn.microsoft.com/library/gg622941\(v=vs.85\).aspx)
14
+ - X-Download-Options - [Prevent file downloads opening](https://msdn.microsoft.com/library/jj542450(v=vs.85).aspx)
10
15
  - 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 Authorites. [Public Key Pinnning 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.
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`
16
+ - 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)
20
17
 
21
- The following methods are going to be called, unless they are provided in a `skip_before_filter` block.
18
+ `secure_headers` is a library with a global config, per request overrides, and rack middleware that enables you customize your application settings.
22
19
 
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`
20
+ ## Use
31
21
 
32
- ### Bonus Features
33
-
34
- This gem makes a few assumptions about how you will use some features. For example:
35
-
36
- * It fills any blank directives with the value in `:default_src` Getting a default\-src report is pretty useless. This way, you will always know what type of violation occurred. You can disable this feature by supplying `:disable_fill_missing => true`. This is referred to as the "effective-directive" in the spec, but is not well supported as of Nov 5, 2013.
22
+ `gem install secure_headers`
37
23
 
38
24
  ## Configuration
39
25
 
40
- **Place the following in an initializer (recommended):**
26
+ 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.
27
+
28
+ All `nil` values will fallback to their default values. `SecureHeaders::OPT_OUT` will disable the header entirely.
41
29
 
42
30
  ```ruby
43
- ::SecureHeaders::Configuration.configure do |config|
44
- config.hsts = {:max_age => 20.years.to_i, :include_subdomains => true}
45
- config.x_frame_options = 'DENY'
31
+ SecureHeaders::Configuration.default do |config|
32
+ config.hsts = "max-age=#{20.years.to_i}"
33
+ config.x_frame_options = "DENY"
46
34
  config.x_content_type_options = "nosniff"
47
- config.x_xss_protection = {:value => 1, :mode => 'block'}
48
- config.x_download_options = 'noopen'
49
- config.x_permitted_cross_domain_policies = 'none'
35
+ config.x_xss_protection = "1; mode=block"
36
+ config.x_download_options = "noopen"
37
+ config.x_permitted_cross_domain_policies = "none"
50
38
  config.csp = {
51
- :default_src => "https: self",
52
- :enforce => proc {|controller| controller.current_user.enforce_csp? },
53
- :frame_src => "https: http:.twimg.com http://itunes.apple.com",
54
- :img_src => "https:",
55
- :report_uri => '//example.com/uri-directive'
39
+ # "meta" values. these will shaped the header, but the values are not included in the header.
40
+ report_only: true, # default: false
41
+ preserve_schemes: true, # default: false. Schemes are removed from host sources to save bytes and discourage mixed content.
42
+
43
+ # directive values: these values will directly translate into source directives
44
+ default_src: %w(https: 'self'),
45
+ frame_src: %w('self' *.twimg.com itunes.apple.com),
46
+ connect_src: %w(wws:),
47
+ font_src: %w('self' data:),
48
+ img_src: %w(mycdn.com data:),
49
+ media_src: %w(utoob.com),
50
+ object_src: %w('self'),
51
+ script_src: %w('self'),
52
+ style_src: %w('unsafe-inline'),
53
+ base_uri: %w('self'),
54
+ child_src: %w('self'),
55
+ form_action: %w('self' github.com),
56
+ frame_ancestors: %w('none'),
57
+ plugin_types: %w(application/x-shockwave-flash),
58
+ block_all_mixed_content: true, # see [http://www.w3.org/TR/mixed-content/](http://www.w3.org/TR/mixed-content/)
59
+ report_uri: %w(https://example.com/uri-directive)
56
60
  }
57
61
  config.hpkp = {
58
- :max_age => 60.days.to_i,
59
- :include_subdomains => true,
60
- :report_uri => '//example.com/uri-directive',
61
- :pins => [
62
- {:sha256 => 'abc'},
63
- {:sha256 => '123'}
62
+ report_only: false,
63
+ max_age: 60.days.to_i,
64
+ include_subdomains: true,
65
+ report_uri: "https://example.com/uri-directive",
66
+ pins: [
67
+ {sha256: "abc"},
68
+ {sha256: "123"}
64
69
  ]
65
70
  }
66
71
  end
67
-
68
- # and then include this in application_controller.rb
69
- class ApplicationController < ActionController::Base
70
- ensure_security_headers
71
- end
72
72
  ```
73
73
 
74
- Or do the config as a parameter to `ensure_security_headers`
74
+ ### rails 2
75
+
76
+ 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.
75
77
 
76
78
  ```ruby
77
- ensure_security_headers(
78
- :hsts => {:include_subdomains => true, :max_age => 20.years.to_i},
79
- :x_frame_options => 'DENY',
80
- :csp => false
81
- )
79
+ use SecureHeaders::Middleware
82
80
  ```
83
81
 
84
- ## Options for ensure\_security\_headers
82
+ ## Default values
85
83
 
86
- **To disable any of these headers, supply a value of false (e.g. :hsts => false), supplying nil will set the default value**
84
+ 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).
87
85
 
88
- Each header configuration can take a hash, or a string, or both. If a string
89
- is provided, that value is inserted verbatim. If a hash is supplied, a
90
- header will be constructed using the supplied options.
86
+ ## Named overrides
91
87
 
92
- ### The Easy Headers
88
+ Named overrides serve two purposes:
93
89
 
94
- This configuration will likely work for most applications without modification.
90
+ * To be able to refer to a configuration by simple name.
91
+ * By precomputing the headers for a named configuration, the headers generated once and reused over every request.
95
92
 
96
- ```ruby
97
- :hsts => {:max_age => 631138519, :include_subdomains => false}
98
- :x_frame_options => {:value => 'SAMEORIGIN'}
99
- :x_xss_protection => {:value => 1, :mode => 'block'} # set the :mode option to false to use "warning only" mode
100
- :x_content_type_options => {:value => 'nosniff'}
101
- :x_download_options => {:value => 'noopen'}
102
- :x_permitted_cross_domain_policies => {:value => 'none'}
103
- ```
104
-
105
- ### Content Security Policy (CSP)
93
+ 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.
106
94
 
107
95
  ```ruby
108
- :csp => {
109
- :enforce => false, # sets header to report-only, by default
110
- # default_src is required!
111
- :default_src => nil, # sets the default-src/allow+options directives
112
-
113
- # 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
114
- :report_uri => '//mysite.example.com',
115
-
116
- # these directives all take 'none', 'self', or a globbed pattern
117
- :img_src => nil,
118
- :frame_src => nil,
119
- :connect_src => nil,
120
- :font_src => nil,
121
- :media_src => nil,
122
- :object_src => nil,
123
- :style_src => nil,
124
- :script_src => nil,
125
-
126
- # http additions will be appended to the various directives when
127
- # over http, relaxing the policy
128
- # e.g.
129
- # :csp => {
130
- # :img_src => 'https:',
131
- # :http_additions => {:img_src => 'http'}
132
- # }
133
- # would produce the directive: "img-src https: http:;"
134
- # when over http, ignored for https requests
135
- :http_additions => {}
136
- }
137
- ```
138
-
139
- ### Example CSP header config
96
+ class ApplicationController < ActionController::Base
97
+ SecureHeaders::Configuration.default do |config|
98
+ config.csp = {
99
+ default_src: %w('self'),
100
+ script_src: %w(example.org)
101
+ }
102
+ end
140
103
 
104
+ # override default configuration
105
+ SecureHeaders::Configuration.override(:script_from_otherdomain_com) do |config|
106
+ config.csp[:script_src] << "otherdomain.com"
107
+ end
141
108
 
142
- ```ruby
143
- # most basic example
144
- :csp => {
145
- :default_src => "https: inline eval",
146
- :report_uri => '/uri-directive'
147
- }
109
+ # overrides the :script_from_otherdomain_com configuration
110
+ SecureHeaders::Configuration.override(:another_config, :script_from_otherdomain_com) do |config|
111
+ config.csp[:script_src] << "evenanotherdomain.com"
112
+ end
113
+ end
148
114
 
149
- > "default-src 'unsafe-inline' 'unsafe-eval' https:; report-uri /uri-directive;"
115
+ class MyController < ApplicationController
116
+ def index
117
+ # Produces default-src 'self'; script-src example.org otherdomain.org
118
+ use_secure_headers_override(:script_from_otherdomain_com)
119
+ end
150
120
 
151
- # turn off inline scripting/eval
152
- :csp => {
153
- :default_src => 'https:',
154
- :report_uri => '/uri-directive'
155
- }
121
+ def show
122
+ # Produces default-src 'self'; script-src example.org otherdomain.org evenanotherdomain.com
123
+ use_secure_headers_override(:another_config)
124
+ end
125
+ end
126
+ ```
156
127
 
157
- > "default-src https:; report-uri /uri-directive;"
128
+ By default, a noop configuration is provided. No headers will be set when this default override is used.
158
129
 
159
- # 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
160
- :csp => {
161
- :default_src => 'self',
162
- :img_src => '*',
163
- :object_src => ['media1.com', 'media2.com', '*.cdn.com'],
164
- # alternatively (NOT csv) :object_src => 'media1.com media2.com *.cdn.com'
165
- :script_src => 'trustedscripts.example.com'
166
- }
167
- "default-src 'self'; img-src *; object-src media1.com media2.com *.cdn.com; script-src trustedscripts.example.com;"
130
+ ```ruby
131
+ class MyController < ApplicationController
132
+ def index
133
+ SecureHeaders::opt_out_of_all_protection(request)
134
+ end
135
+ end
168
136
  ```
169
137
 
170
- ### Tagging Requests
138
+ ## Per-action configuration
171
139
 
172
- 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"
140
+ 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.
173
141
 
174
142
  ```ruby
175
- {
176
- :tag_report_uri => true,
177
- :enforce => true,
178
- :app_name => 'twitter',
179
- :report_uri => 'csp_reports'
180
- }
181
- ```
182
-
183
- Results in
184
- ```
185
- report-uri csp_reports?enforce=true&app_name=twitter
143
+ # Given a config of:
144
+ ::SecureHeaders::Configuration.default do |config|
145
+ config.csp = {
146
+ default_src: %w('self'),
147
+ script_src: %w('self')
148
+ }
149
+ end
150
+
151
+ class MyController < ApplicationController
152
+ def index
153
+ # Append value to the source list, override 'none' values
154
+ # Produces: default-src 'self'; script-src 'self' s3.amazaonaws.com; object-src 'self' youtube.com
155
+ append_content_security_policy_directives(script_src: %w(s3.amazaonaws.com), object_src: %w('self' youtube.com))
156
+
157
+ # Overrides the previously set source list, override 'none' values
158
+ # Produces: default-src 'self'; script-src s3.amazaonaws.com; object-src 'self'
159
+ override_content_security_policy_directives(script_src: %w(s3.amazaonaws.com), object_src: %w('self'))
160
+
161
+ # Global settings default to "sameorigin"
162
+ override_x_frame_options("DENY")
163
+ end
186
164
  ```
187
165
 
188
- ### CSP Level 2 features
166
+ 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.
167
+ * `append_content_security_policy_directives(hash)`: appends each value to the corresponding CSP app-wide configuration.
168
+ * `override_content_security_policy_directives(hash)`: merges the hash into the app-wide configuration, overwriting any previous config
169
+ * `override_x_frame_options(value)`: sets the `X-Frame-Options header` to `value`
189
170
 
190
- *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.*
171
+ ## Appending / overriding Content Security Policy
191
172
 
192
- #### Nonce
173
+ 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:)}`.
193
174
 
194
- 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.
175
+ #### Append to the policy with a directive other than `default_src`
195
176
 
196
- 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.
177
+ 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:
197
178
 
198
179
  ```ruby
199
- :csp => {
200
- :default_src => 'self',
201
- :script_src => 'self nonce'
202
- }
203
- ```
180
+ ::SecureHeaders::Configuration.default do |config|
181
+ config.csp = {
182
+ default_src: %w('self')
183
+ }
184
+ end
185
+ ```
186
+
187
+ Code | Result
188
+ ------------- | -------------
189
+ `append_content_security_policy_directives(script_src: %w(mycdn.com))` | `default-src 'self'; script-src 'self' mycdn.com`
190
+ `override_content_security_policy_directives(script_src: %w(mycdn.com))` | `default-src 'self'; script-src mycdn.com`
204
191
 
205
- > content-security-policy: default-src 'self'; script-src 'self' 'nonce-abc123' 'unsafe-inline'
192
+ #### Nonce
193
+
194
+ 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.
195
+
196
+ 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.
206
197
 
207
198
  ```erb
208
- <script nonce="<%= @content_security_policy_nonce %>">
199
+ <script nonce="<%= content_security_policy_nonce %>">
209
200
  console.log("whitelisted, will execute")
210
201
  </script>
211
202
 
@@ -217,12 +208,19 @@ Setting a nonce will also set 'unsafe-inline' for browsers that don't support no
217
208
  console.log("won't execute, not whitelisted")
218
209
  </script>
219
210
  ```
211
+
220
212
  You can use a view helper to automatically add nonces to script tags:
213
+
221
214
  ```erb
222
215
  <%= nonced_javascript_tag do %>
223
- console.log("nonced!")
216
+ console.log("hai");
217
+ <% end %>
218
+
219
+ <%= nonced_style_tag do %>
220
+ body {
221
+ background-color: black;
222
+ }
224
223
  <% end %>
225
- <%= nonced_javascript_tag("nonced without a block!") %>
226
224
  ```
227
225
 
228
226
  becomes:
@@ -231,90 +229,22 @@ becomes:
231
229
  <script nonce="/jRAxuLJsDXAxqhNBB7gg7h55KETtDQBXe4ZL+xIXwI=">
232
230
  console.log("nonced!")
233
231
  </script>
232
+ <style nonce="/jRAxuLJsDXAxqhNBB7gg7h55KETtDQBXe4ZL+xIXwI=">
233
+ body {
234
+ background-color: black;
235
+ }
236
+ </style>
234
237
  ```
235
238
 
236
239
  #### Hash
237
240
 
238
- 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.
239
-
240
- 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.
241
-
242
- If you only have a few hashes, you can hardcode them for the entire app:
243
-
244
- ```ruby
245
- config.csp = {
246
- :default_src => "https:",
247
- :script_src => 'self'
248
- :script_hashes => ['sha1-abc', 'sha1-qwe']
249
- }
250
- ```
251
-
252
- The following will work as well, but may not be as clear:
253
-
254
- ```ruby
255
- config.csp = {
256
- :default_src => "https:",
257
- :script_src => "self 'sha1-qwe'"
258
- }
259
- ```
260
-
261
- 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:
262
-
263
- ```ruby
264
- # config.ru
265
- require 'secure_headers/headers/content_security_policy/script_hash_middleware'
266
- use ::SecureHeaders::ContentSecurityPolicy::ScriptHashMiddleware
267
- ```
268
-
269
- ```ruby
270
- config.csp = {
271
- :default_src => "https:",
272
- :script_src => 'self',
273
- :script_hash_middleware => true
274
- }
275
- ```
276
-
277
- 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:
278
-
279
- ```$ bundle exec rake secure_headers:generate_hashes```
280
-
281
- Which will generate something like:
282
-
283
- ```yaml
284
- # config/script_hashes.yml
285
- app/views/layouts/application.html.erb:
286
- - sha256-l8OLjZqYRnKilpdE0VosRMvhdYArjXT4NZaK2p7QVvs=
287
- app/templates/articles/edit.html.erb:
288
- - sha256-+7mij1/uCwtCQRWrof2NmOln5qX+5WdVwTLMpi8nuoA=
289
- - sha256-Ny4TRIhhFpnYnSeKC274P6bfAz4TOkezLabavIAU4dA=
290
- - sha256-I5e58Gqbu4WpO9dck18QxO7aYOHKrELIi70it4jIPi0=
291
- - sha256-Po4LMynwnAJHxiTp3DQaQ3YDBj3paN/xrDoKl4OyxY4=
292
- ```
293
-
294
- In this example, if we visit /articles/edit/[id], the above hashes will automatically be added to the CSP header's
295
- script-src value!
296
-
297
- You can use plain "script" tags or you can use a built-in helper:
298
-
299
- ```erb
300
- <%= hashed_javascript_tag do %>
301
- console.log("hashed automatically!")
302
- <% end %>
303
- ```
304
-
305
- 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:
306
-
307
- ```erb
308
- <%= hashed_javascript_tag(raise_error_on_unrecognized_hash = true) do %>
309
- console.log("will raise an exception if not in script_hashes.yml!")
310
- <% end %>
311
- ```
241
+ The hash feature has been removed, for now.
312
242
 
313
243
  ### Public Key Pins
314
244
 
315
245
  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.
316
246
 
317
- ```
247
+ ```ruby
318
248
  config.hpkp = {
319
249
  max_age: 60.days.to_i, # max_age is a required parameter
320
250
  include_subdomains: true, # whether or not to apply pins to subdomains
@@ -323,7 +253,7 @@ config.hpkp = {
323
253
  {sha256: 'b5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b878ae4944c'},
324
254
  {sha256: '73a2c64f9545172c1195efb6616ca5f7afd1df6f245407cafb90de3998a1c97f'}
325
255
  ],
326
- enforce: true, # defaults to false (report-only mode)
256
+ report_only: true, # defaults to false (report-only mode)
327
257
  report_uri: '//example.com/uri-directive',
328
258
  app_name: 'example',
329
259
  tag_report_uri: true
@@ -340,28 +270,17 @@ require 'sinatra'
340
270
  require 'haml'
341
271
  require 'secure_headers'
342
272
 
343
- ::SecureHeaders::Configuration.configure do |config|
344
- config.hsts = {:max_age => 99, :include_subdomains => true}
345
- config.x_frame_options = 'DENY'
346
- config.x_content_type_options = "nosniff"
347
- config.x_xss_protection = {:value => 1, :mode => false}
348
- config.x_download_options = 'noopen'
349
- config.x_permitted_cross_domain_policies = 'none'
350
- config.csp = {
351
- :default_src => "https: inline eval",
352
- :report_uri => '//example.com/uri-directive',
353
- :img_src => "https: data:",
354
- :frame_src => "https: http:.twimg.com http://itunes.apple.com"
355
- }
356
- config.hpkp = false
273
+ use SecureHeaders::Middleware
274
+
275
+ SecureHeaders::Configuration.default do |config|
276
+ ...
357
277
  end
358
278
 
359
279
  class Donkey < Sinatra::Application
360
- include SecureHeaders
361
280
  set :root, APP_ROOT
362
281
 
363
282
  get '/' do
364
- set_csp_header
283
+ SecureHeaders.override_x_frame_options(SecureHeaders::OPT_OUT)
365
284
  haml :index
366
285
  end
367
286
  end
@@ -374,12 +293,13 @@ You can use SecureHeaders for Padrino applications as well:
374
293
  In your `Gemfile`:
375
294
 
376
295
  ```ruby
377
- gem "secure_headers", :require => 'secure_headers'
296
+ gem "secure_headers", require: 'secure_headers'
378
297
  ```
379
298
 
380
299
  then in your `app.rb` file you can:
381
300
 
382
301
  ```ruby
302
+ Padrino.use(SecureHeaders::Middleware)
383
303
  require 'secure_headers/padrino'
384
304
 
385
305
  module Web
@@ -387,7 +307,6 @@ module Web
387
307
  register SecureHeaders::Padrino
388
308
 
389
309
  get '/' do
390
- set_csp_header
391
310
  render 'index'
392
311
  end
393
312
  end
@@ -398,43 +317,22 @@ and in `config/boot.rb`:
398
317
 
399
318
  ```ruby
400
319
  def before_load
401
- ::SecureHeaders::Configuration.configure do |config|
402
- config.hsts = {:max_age => 99, :include_subdomains => true}
403
- config.x_frame_options = 'DENY'
404
- config.x_content_type_options = "nosniff"
405
- config.x_xss_protection = {:value => '1', :mode => false}
406
- config.x_download_options = 'noopen'
407
- config.x_permitted_cross_domain_policies = 'none'
408
- config.csp = {
409
- :default_src => "https: inline eval",
410
- :report_uri => '//example.com/uri-directive',
411
- :img_src => "https: data:",
412
- :frame_src => "https: http:.twimg.com http://itunes.apple.com"
413
- }
320
+ SecureHeaders::Configuration.default do |config|
321
+ ...
414
322
  end
415
323
  end
416
324
  ```
417
325
 
418
326
  ## Similar libraries
419
327
 
420
- * Node.js (express) [helmet](https://github.com/evilpacket/helmet) and [hood](https://github.com/seanmonstar/hood)
328
+ * Rack [rack-secure_headers](https://github.com/frodsan/rack-secure_headers)
329
+ * Node.js (express) [helmet](https://github.com/helmetjs/helmet) and [hood](https://github.com/seanmonstar/hood)
421
330
  * Node.js (hapi) [blankie](https://github.com/nlf/blankie)
422
331
  * J2EE Servlet >= 3.0 [headlines](https://github.com/sourceclear/headlines)
423
332
  * ASP.NET - [NWebsec](https://github.com/NWebsec/NWebsec/wiki)
424
333
  * Python - [django-csp](https://github.com/mozilla/django-csp) + [commonware](https://github.com/jsocol/commonware/); [django-security](https://github.com/sdelements/django-security)
425
334
  * Go - [secureheader](https://github.com/kr/secureheader)
426
-
427
- ## Authors
428
-
429
- * Neil Matatall [@ndm](https://twitter.com/ndm) - primary author.
430
- * Nicholas Green [@nickgreen](https://twitter.com/nickgreen) - code contributions, main reviewer.
431
-
432
- ## Acknowledgements
433
-
434
- * Justin Collins [@presidentbeef](https://twitter.com/presidentbeef) & Jim O'Leary [@jimio](https://twitter.com/jimio) for reviews.
435
- * 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.
436
- * Sumit Shah [@omnidactyl](https://twitter.com/omnidactyl) - For being an eager guinea pig.
437
- * Chris Aniszczyk [@cra](https://twitter.com/cra) - For running an awesome open source program at Twitter.
335
+ * Elixir [secure_headers](https://github.com/anotherhale/secure_headers)
438
336
 
439
337
  ## License
440
338
 
data/Rakefile CHANGED
@@ -7,44 +7,10 @@ require 'net/https'
7
7
  desc "Run RSpec"
8
8
  RSpec::Core::RakeTask.new do |t|
9
9
  t.verbose = false
10
- t.rspec_opts = "--format progress"
10
+ t.rspec_opts = "--format progress"
11
11
  end
12
12
 
13
- task :default => :all_spec
14
-
15
- desc "Run all specs, and test fixture apps"
16
- task :all_spec => :spec do
17
- pwd = Dir.pwd
18
- Dir.chdir 'fixtures/rails_3_2_12'
19
- puts Dir.pwd
20
- str = `bundle install >> /dev/null; bundle exec rspec spec`
21
- puts str
22
- unless $? == 0
23
- Dir.chdir pwd
24
- fail "Header tests with app not using initializer failed exit code: #{$?}"
25
- end
26
-
27
- Dir.chdir pwd
28
- Dir.chdir 'fixtures/rails_3_2_12_no_init'
29
- puts Dir.pwd
30
- puts `bundle install >> /dev/null; bundle exec rspec spec`
31
-
32
- unless $? == 0
33
- fail "Header tests with app not using initializer failed"
34
- Dir.chdir pwd
35
- end
36
-
37
- Dir.chdir pwd
38
- Dir.chdir 'fixtures/rails_4_1_8'
39
- puts Dir.pwd
40
- puts `bundle install >> /dev/null; bundle exec rspec spec`
41
-
42
- unless $? == 0
43
- fail "Header tests with Rails 4 failed"
44
- Dir.chdir pwd
45
- end
46
-
47
- end
13
+ task default: :spec
48
14
 
49
15
  begin
50
16
  require 'rdoc/task'