secure_headers 1.1.1 → 2.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 (125) hide show
  1. data/.gitignore +4 -8
  2. data/.ruby-gemset +1 -0
  3. data/.ruby-version +1 -0
  4. data/.travis.yml +8 -3
  5. data/Gemfile +6 -11
  6. data/README.md +184 -101
  7. data/Rakefile +11 -116
  8. data/fixtures/rails_3_2_12/Gemfile +0 -8
  9. data/fixtures/rails_3_2_12/app/controllers/things_controller.rb +0 -1
  10. data/fixtures/rails_3_2_12/app/views/layouts/application.html.erb +1 -4
  11. data/fixtures/rails_3_2_12/app/views/other_things/index.html.erb +2 -1
  12. data/fixtures/rails_3_2_12/app/views/things/index.html.erb +1 -21
  13. data/fixtures/rails_3_2_12/config/application.rb +0 -54
  14. data/fixtures/rails_3_2_12/config/environments/test.rb +2 -2
  15. data/fixtures/rails_3_2_12/config/initializers/secure_headers.rb +3 -1
  16. data/fixtures/rails_3_2_12/config/routes.rb +0 -57
  17. data/fixtures/rails_3_2_12/config/script_hashes.yml +5 -0
  18. data/fixtures/rails_3_2_12/config.ru +3 -0
  19. data/fixtures/rails_3_2_12/spec/controllers/other_things_controller_spec.rb +57 -19
  20. data/fixtures/rails_3_2_12/spec/controllers/things_controller_spec.rb +18 -13
  21. data/fixtures/rails_3_2_12/spec/spec_helper.rb +1 -5
  22. data/fixtures/rails_3_2_12_no_init/Gemfile +0 -9
  23. data/fixtures/rails_3_2_12_no_init/app/controllers/other_things_controller.rb +1 -2
  24. data/fixtures/rails_3_2_12_no_init/app/controllers/things_controller.rb +1 -1
  25. data/fixtures/rails_3_2_12_no_init/app/views/layouts/application.html.erb +0 -2
  26. data/fixtures/rails_3_2_12_no_init/app/views/things/index.html.erb +0 -21
  27. data/fixtures/rails_3_2_12_no_init/config/application.rb +0 -51
  28. data/fixtures/rails_3_2_12_no_init/config/environments/test.rb +2 -2
  29. data/fixtures/rails_3_2_12_no_init/config/routes.rb +0 -57
  30. data/fixtures/rails_3_2_12_no_init/spec/controllers/other_things_controller_spec.rb +17 -12
  31. data/fixtures/rails_3_2_12_no_init/spec/controllers/things_controller_spec.rb +17 -12
  32. data/fixtures/rails_3_2_12_no_init/spec/spec_helper.rb +3 -18
  33. data/fixtures/rails_4_1_8/Gemfile +5 -0
  34. data/fixtures/rails_4_1_8/README.rdoc +28 -0
  35. data/fixtures/rails_4_1_8/Rakefile +6 -0
  36. data/fixtures/rails_4_1_8/app/controllers/application_controller.rb +4 -0
  37. data/fixtures/rails_4_1_8/app/controllers/concerns/.keep +0 -0
  38. data/fixtures/rails_4_1_8/app/controllers/other_things_controller.rb +5 -0
  39. data/fixtures/rails_4_1_8/app/controllers/things_controller.rb +5 -0
  40. data/fixtures/rails_4_1_8/app/models/.keep +0 -0
  41. data/fixtures/rails_4_1_8/app/models/concerns/.keep +0 -0
  42. data/fixtures/rails_4_1_8/app/views/layouts/application.html.erb +11 -0
  43. data/fixtures/rails_4_1_8/app/views/other_things/index.html.erb +2 -0
  44. data/fixtures/rails_4_1_8/app/views/things/index.html.erb +1 -0
  45. data/fixtures/rails_4_1_8/config/application.rb +15 -0
  46. data/fixtures/rails_4_1_8/config/boot.rb +4 -0
  47. data/fixtures/rails_4_1_8/config/environment.rb +5 -0
  48. data/fixtures/rails_4_1_8/config/environments/test.rb +10 -0
  49. data/fixtures/rails_4_1_8/config/initializers/secure_headers.rb +17 -0
  50. data/fixtures/rails_4_1_8/config/routes.rb +4 -0
  51. data/fixtures/rails_4_1_8/config/script_hashes.yml +5 -0
  52. data/fixtures/rails_4_1_8/config/secrets.yml +22 -0
  53. data/fixtures/rails_4_1_8/config.ru +4 -0
  54. data/fixtures/rails_4_1_8/lib/assets/.keep +0 -0
  55. data/fixtures/rails_4_1_8/lib/tasks/.keep +0 -0
  56. data/fixtures/rails_4_1_8/log/.keep +0 -0
  57. data/fixtures/rails_4_1_8/spec/controllers/other_things_controller_spec.rb +83 -0
  58. data/fixtures/rails_4_1_8/spec/controllers/things_controller_spec.rb +59 -0
  59. data/fixtures/rails_4_1_8/spec/spec_helper.rb +15 -0
  60. data/fixtures/rails_4_1_8/vendor/assets/javascripts/.keep +0 -0
  61. data/fixtures/rails_4_1_8/vendor/assets/stylesheets/.keep +0 -0
  62. data/lib/secure_headers/hash_helper.rb +7 -0
  63. data/lib/secure_headers/headers/content_security_policy/script_hash_middleware.rb +22 -0
  64. data/lib/secure_headers/headers/content_security_policy.rb +154 -129
  65. data/lib/secure_headers/headers/strict_transport_security.rb +2 -1
  66. data/lib/secure_headers/headers/x_download_options.rb +39 -0
  67. data/lib/secure_headers/headers/x_permitted_cross_domain_policies.rb +40 -0
  68. data/lib/secure_headers/headers/x_xss_protection.rb +2 -1
  69. data/lib/secure_headers/padrino.rb +14 -0
  70. data/lib/secure_headers/railtie.rb +19 -24
  71. data/lib/secure_headers/version.rb +1 -1
  72. data/lib/secure_headers/view_helper.rb +68 -0
  73. data/lib/secure_headers.rb +65 -16
  74. data/lib/tasks/tasks.rake +48 -0
  75. data/secure_headers.gemspec +4 -4
  76. data/spec/lib/secure_headers/headers/content_security_policy/script_hash_middleware_spec.rb +47 -0
  77. data/spec/lib/secure_headers/headers/content_security_policy_spec.rb +127 -220
  78. data/spec/lib/secure_headers/headers/strict_transport_security_spec.rb +21 -20
  79. data/spec/lib/secure_headers/headers/x_content_type_options_spec.rb +12 -12
  80. data/spec/lib/secure_headers/headers/x_download_options_spec.rb +32 -0
  81. data/spec/lib/secure_headers/headers/x_frame_options_spec.rb +12 -12
  82. data/spec/lib/secure_headers/headers/x_permitted_cross_domain_policies_spec.rb +64 -0
  83. data/spec/lib/secure_headers/headers/x_xss_protection_spec.rb +20 -19
  84. data/spec/lib/secure_headers_spec.rb +69 -68
  85. data/spec/spec_helper.rb +29 -18
  86. metadata +51 -46
  87. data/.rvmrc +0 -1
  88. data/Guardfile +0 -10
  89. data/HISTORY.md +0 -115
  90. data/app/controllers/content_security_policy_controller.rb +0 -75
  91. data/config/curl-ca-bundle.crt +0 -5420
  92. data/config/routes.rb +0 -3
  93. data/fixtures/rails_3_2_12/Guardfile +0 -14
  94. data/fixtures/rails_3_2_12/app/models/thing.rb +0 -3
  95. data/fixtures/rails_3_2_12/config/database.yml +0 -25
  96. data/fixtures/rails_3_2_12/config/environments/development.rb +0 -37
  97. data/fixtures/rails_3_2_12/config/environments/production.rb +0 -67
  98. data/fixtures/rails_3_2_12/config/initializers/backtrace_silencers.rb +0 -7
  99. data/fixtures/rails_3_2_12/config/initializers/inflections.rb +0 -15
  100. data/fixtures/rails_3_2_12/config/initializers/mime_types.rb +0 -5
  101. data/fixtures/rails_3_2_12/config/initializers/secret_token.rb +0 -7
  102. data/fixtures/rails_3_2_12/config/initializers/session_store.rb +0 -8
  103. data/fixtures/rails_3_2_12/config/initializers/wrap_parameters.rb +0 -14
  104. data/fixtures/rails_3_2_12/config/locales/en.yml +0 -5
  105. data/fixtures/rails_3_2_12/db/schema.rb +0 -16
  106. data/fixtures/rails_3_2_12/db/seeds.rb +0 -7
  107. data/fixtures/rails_3_2_12_no_init/Guardfile +0 -14
  108. data/fixtures/rails_3_2_12_no_init/app/models/thing.rb +0 -3
  109. data/fixtures/rails_3_2_12_no_init/app/views/things/_form.html.erb +0 -17
  110. data/fixtures/rails_3_2_12_no_init/app/views/things/edit.html.erb +0 -6
  111. data/fixtures/rails_3_2_12_no_init/app/views/things/new.html.erb +0 -5
  112. data/fixtures/rails_3_2_12_no_init/app/views/things/show.html.erb +0 -5
  113. data/fixtures/rails_3_2_12_no_init/config/database.yml +0 -25
  114. data/fixtures/rails_3_2_12_no_init/config/environments/development.rb +0 -37
  115. data/fixtures/rails_3_2_12_no_init/config/environments/production.rb +0 -67
  116. data/fixtures/rails_3_2_12_no_init/config/initializers/backtrace_silencers.rb +0 -7
  117. data/fixtures/rails_3_2_12_no_init/config/initializers/inflections.rb +0 -15
  118. data/fixtures/rails_3_2_12_no_init/config/initializers/mime_types.rb +0 -5
  119. data/fixtures/rails_3_2_12_no_init/config/initializers/secret_token.rb +0 -7
  120. data/fixtures/rails_3_2_12_no_init/config/initializers/session_store.rb +0 -8
  121. data/fixtures/rails_3_2_12_no_init/config/initializers/wrap_parameters.rb +0 -14
  122. data/fixtures/rails_3_2_12_no_init/config/locales/en.yml +0 -5
  123. data/fixtures/rails_3_2_12_no_init/db/schema.rb +0 -16
  124. data/fixtures/rails_3_2_12_no_init/db/seeds.rb +0 -7
  125. data/spec/controllers/content_security_policy_controller_spec.rb +0 -90
data/.gitignore CHANGED
@@ -1,4 +1,5 @@
1
1
  *.gem
2
+ *.DS_STORE
2
3
  *.rbc
3
4
  .bundle
4
5
  .config
@@ -15,12 +16,7 @@ rdoc
15
16
  spec/reports
16
17
  test/tmp
17
18
  test/version_tmp
18
- tmp
19
-
20
- /fixtures/rails_3_2_12/log/test.log
21
- /fixtures/rails_3_2_12_no_init/log/test.log
19
+ *tmp
22
20
  *.sqlite3
23
- test.sqlite3
24
- *test.sqlite3
25
- /fixtures/rails_3_2_12_no_init/db/test.sqlite3
26
- /fixtures/rails_3_2_12/db/test.sqlite3
21
+ fixtures/rails_3_2_12_no_init/log
22
+ fixtures/rails_3_2_12/log
data/.ruby-gemset ADDED
@@ -0,0 +1 @@
1
+ secureheaders
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ ruby-1.9.3-p484
data/.travis.yml CHANGED
@@ -1,6 +1,11 @@
1
+ language: ruby
2
+
1
3
  rvm:
4
+ - "2.1"
5
+ - "2.0.0"
2
6
  - "1.9.3"
3
7
  - "1.8.7"
4
- # - jruby-19mode
5
- # - jruby-18mode
6
- #script: ./travis.sh
8
+ - "jruby-19mode"
9
+
10
+ sudo: false
11
+ cache: bundler
data/Gemfile CHANGED
@@ -4,17 +4,12 @@ gemspec
4
4
 
5
5
  group :test do
6
6
  gem 'rails', '3.2.12'
7
- gem 'sqlite3', :platform => [:ruby, :mswin, :mingw]
8
- gem 'jdbc-sqlite3', :platform => :jruby
9
- gem 'rspec-rails'
10
- gem 'spork'
11
- gem 'pry'
12
- gem 'rspec'
13
- gem 'guard-spork', :platform => :ruby_19
14
- gem 'guard-rspec', :platform => :ruby_19
7
+ gem 'sqlite3', :platforms => [:ruby, :mswin, :mingw]
8
+ gem 'jdbc-sqlite3', :platforms => [:jruby]
9
+ gem 'rspec-rails', '>= 3.1'
10
+ gem 'rspec', '>= 3.1'
15
11
  gem 'growl'
16
12
  gem 'rb-fsevent'
17
- gem 'simplecov'
18
- gem 'debugger', :platform => :ruby_19
19
- gem 'ruby-debug', :platform => :ruby_18
13
+ gem 'coveralls', :platforms => [:ruby_19]
14
+ gem 'i18n', '< 0.7.0', :platforms => [:ruby_18]
20
15
  end
data/README.md CHANGED
@@ -1,4 +1,4 @@
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)
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
3
  The gem will automatically apply several headers that are related to security. This includes:
4
4
  - Content Security Policy (CSP) - Helps detect/prevent XSS, mixed-content, and other classes of attack. [CSP 1.1 Specification](https://dvcs.w3.org/hg/content-security-policy/raw-file/tip/csp-specification.dev.html)
@@ -6,36 +6,12 @@ The gem will automatically apply several headers that are related to security.
6
6
  - 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
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
8
  - X-Content-Type-Options - [Prevent content type sniffing](http://msdn.microsoft.com/en-us/library/ie/gg622941\(v=vs.85\).aspx)
9
-
10
- This gem has integration with Rails, but works for any Ruby code. See the sinatra example section.
11
-
12
- ## Installation
13
-
14
- Add to your Gemfile
15
-
16
- ```ruby
17
- gem 'secure_headers'
18
- ```
19
-
20
- And then execute:
21
-
22
- ```console
23
- $ bundle
24
- ```
25
-
26
- Or install it yourself as:
27
-
28
- ```console
29
- $ gem install secure_headers
30
- ```
9
+ - X-Download-Options - [Prevent file downloads opening](http://msdn.microsoft.com/en-us/library/ie/jj542450(v=vs.85).aspx)
10
+ - 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)
31
11
 
32
12
  ## Usage
33
13
 
34
- Functionality provided
35
-
36
- - `ensure_security_headers`: will set security-related headers automatically based on the configuration below.
37
-
38
- By default, it will set all of the headers listed in the options section below unless specified.
14
+ - `ensure_security_headers` in a controller will set security-related headers automatically based on the configuration below.
39
15
 
40
16
  ### Disabling
41
17
 
@@ -48,14 +24,14 @@ The following methods are going to be called, unless they are provided in a `ski
48
24
  * `:set_x_frame_options_header`
49
25
  * `:set_x_xss_protection_header`
50
26
  * `:set_x_content_type_options_header`
27
+ * `:set_x_download_options_header`
28
+ * `:set_x_permitted_cross_domain_policies_header`
51
29
 
52
30
  ### Bonus Features
53
31
 
54
32
  This gem makes a few assumptions about how you will use some features. For example:
55
33
 
56
34
  * 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.
57
- * Firefox does not support cross\-origin CSP reports. If we are using Firefox, AND the value for `:report_uri` does not satisfy the same\-origin requirements, we will instead forward to an internal endpoint (`FF_CSP_ENDPOINT`). This is also the case if `:report_uri` only contains a path, which we assume will be cross host. This endpoint will in turn forward the request to the value in `:forward_endpoint` without restriction. More information can be found in the "Note on Firefox handling of CSP" section.
58
-
59
35
 
60
36
  ## Configuration
61
37
 
@@ -67,10 +43,12 @@ This gem makes a few assumptions about how you will use some features. For exam
67
43
  config.x_frame_options = 'DENY'
68
44
  config.x_content_type_options = "nosniff"
69
45
  config.x_xss_protection = {:value => 1, :mode => 'block'}
46
+ config.x_download_options = 'noopen'
47
+ config.x_permitted_cross_domain_policies = 'none'
70
48
  config.csp = {
71
- :default_src => "https://* self",
72
- :frame_src => "https://* http://*.twimg.com http://itunes.apple.com",
73
- :img_src => "https://*",
49
+ :default_src => "https: self",
50
+ :frame_src => "https: http:.twimg.com http://itunes.apple.com",
51
+ :img_src => "https:",
74
52
  :report_uri => '//example.com/uri-directive'
75
53
  }
76
54
  end
@@ -85,9 +63,10 @@ Or simply add it to application controller
85
63
 
86
64
  ```ruby
87
65
  ensure_security_headers(
88
- :hsts => {:include_subdomains, :x_frame_options => false},
66
+ :hsts => {:include_subdomains => true, :max_age => 20.years.to_i},
89
67
  :x_frame_options => 'DENY',
90
- :csp => false)
68
+ :csp => false
69
+ )
91
70
  ```
92
71
 
93
72
  ## Options for ensure\_security\_headers
@@ -107,14 +86,12 @@ This configuration will likely work for most applications without modification.
107
86
  :x_frame_options => {:value => 'SAMEORIGIN'}
108
87
  :x_xss_protection => {:value => 1, :mode => 'block'} # set the :mode option to false to use "warning only" mode
109
88
  :x_content_type_options => {:value => 'nosniff'}
89
+ :x_download_options => {:value => 'noopen'}
90
+ :x_permitted_cross_domain_policies => {:value => 'none'}
110
91
  ```
111
92
 
112
93
  ### Content Security Policy (CSP)
113
94
 
114
- All browsers will receive the webkit csp header except Firefox, which gets its own header.
115
- See [WebKit specification](http://www.w3.org/TR/CSP/)
116
- and [Mozilla CSP specification](https://wiki.mozilla.org/Security/CSP/Specification)
117
-
118
95
  ```ruby
119
96
  :csp => {
120
97
  :enforce => false, # sets header to report-only, by default
@@ -124,11 +101,6 @@ and [Mozilla CSP specification](https://wiki.mozilla.org/Security/CSP/Specificat
124
101
  # 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
125
102
  :report_uri => '//mysite.example.com',
126
103
 
127
- # Send reports that cannot be sent across host here. These requests are sent
128
- # the server, not the browser. If no value is supplied, it will default to
129
- # the value in report_uri. Use this if you cannot use relative protocols mentioned above due to host mismatches.
130
- :forward_endpoint => 'https://internal.mylogaggregator.example.com'
131
-
132
104
  # these directives all take 'none', 'self', or a globbed pattern
133
105
  :img_src => nil,
134
106
  :frame_src => nil,
@@ -143,32 +115,12 @@ and [Mozilla CSP specification](https://wiki.mozilla.org/Security/CSP/Specificat
143
115
  # over http, relaxing the policy
144
116
  # e.g.
145
117
  # :csp => {
146
- # :img_src => 'https://*',
147
- # :http_additions => {:img_src => 'http//*'}
118
+ # :img_src => 'https:',
119
+ # :http_additions => {:img_src => 'http'}
148
120
  # }
149
- # would produce the directive: "img-src https://* http://*;"
121
+ # would produce the directive: "img-src https: http:;"
150
122
  # when over http, ignored for https requests
151
123
  :http_additions => {}
152
-
153
- # If you have enforce => true, you can use the `experiments` block to
154
- # also produce a report-only header. Values in this block override the
155
- # parent config for the report-only, and leave the enforcing header
156
- # unaltered. http_additions work the same way described above, but
157
- # are added to your report-only header as expected.
158
- :experimental => {
159
- :script_src => 'self',
160
- :img_src => 'https://mycdn.example.com',
161
- :http_additions {
162
- :img_src => 'http://mycdn.example.com'
163
- }
164
- }
165
-
166
- # script-nonce is an experimental feature of CSP 1.1 available in Chrome. It allows
167
- # you to whitelist inline script blocks. For more information, see
168
- # https://dvcs.w3.org/hg/content-security-policy/raw-file/tip/csp-specification.dev.html#script-nonce
169
- :script_nonce => lambda { @script_nonce = SecureRandom.hex }
170
- # which can be used to whitelist a script block:
171
- # script_tag :nonce = @script_nonce { inline_script_call() }
172
124
  }
173
125
  ```
174
126
 
@@ -178,19 +130,19 @@ and [Mozilla CSP specification](https://wiki.mozilla.org/Security/CSP/Specificat
178
130
  ```ruby
179
131
  # most basic example
180
132
  :csp => {
181
- :default_src => "https://* inline eval",
133
+ :default_src => "https: inline eval",
182
134
  :report_uri => '/uri-directive'
183
135
  }
184
136
 
185
- > "default-src 'unsafe-inline' 'unsafe-eval' https://*; report-uri /uri-directive;"
137
+ > "default-src 'unsafe-inline' 'unsafe-eval' https:; report-uri /uri-directive;"
186
138
 
187
139
  # turn off inline scripting/eval
188
140
  :csp => {
189
- :default_src => 'https://*',
141
+ :default_src => 'https:',
190
142
  :report_uri => '/uri-directive'
191
143
  }
192
144
 
193
- > "default-src https://*; report-uri /uri-directive;"
145
+ > "default-src https:; report-uri /uri-directive;"
194
146
 
195
147
  # 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
196
148
  :csp => {
@@ -203,30 +155,149 @@ and [Mozilla CSP specification](https://wiki.mozilla.org/Security/CSP/Specificat
203
155
  "default-src 'self'; img-src *; object-src media1.com media2.com *.cdn.com; script-src trustedscripts.example.com;"
204
156
  ```
205
157
 
206
- ## Note on Firefox handling of CSP
158
+ ### Tagging Requests
159
+
160
+ 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"
161
+
162
+ ```ruby
163
+ {
164
+ :tag_report_uri => true,
165
+ :enforce => true,
166
+ :app_name => 'twitter',
167
+ :report_uri => 'csp_reports'
168
+ }
169
+ ```
170
+
171
+ Results in
172
+ ```
173
+ report-uri csp_reports?enforce=true&app_name=twitter
174
+ ```
175
+
176
+ ### CSP Level 2 features
177
+
178
+ *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.*
179
+
180
+ #### Nonce
181
+
182
+ 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.
183
+
184
+ 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.
185
+
186
+ ```ruby
187
+ :csp => {
188
+ :default_src => 'self',
189
+ :script_src => 'self nonce'
190
+ }
191
+ ```
192
+
193
+ > content-security-policy: default-src 'self'; script-src 'self' 'nonce-abc123' 'unsafe-inline'
207
194
 
208
- Currently, Firefox does not support the w3c draft standard. So there are a few steps taken to make the two interchangeable.
195
+ ```erb
196
+ <script nonce="<%= @content_security_policy_nonce %>">
197
+ console.log("whitelisted, will execute")
198
+ </script>
209
199
 
210
- * CSP reports will not POST cross\-origin. This sets up an internal endpoint in the application that will forward the request. Set the `forward_endpoint` value in the CSP section if you need to post cross origin for firefox. The internal endpoint that receives the initial request will forward the request to `forward_endpoint`
200
+ <script nonce="lol">
201
+ console.log("won't execute, not whitelisted")
202
+ </script>
211
203
 
212
- ### Adding the Firefox report forwarding endpoint
204
+ <script>
205
+ console.log("won't execute, not whitelisted")
206
+ </script>
207
+ ```
208
+ You can use a view helper to automatically add nonces to script tags:
209
+ ```erb
210
+ <%= nonced_javascript_tag do %>
211
+ console.log("nonced!")
212
+ <% end %>
213
+ <%= nonced_javascript_tag("nonced without a block!") %>
214
+ ```
215
+
216
+ becomes:
217
+
218
+ ```html
219
+ <script nonce="/jRAxuLJsDXAxqhNBB7gg7h55KETtDQBXe4ZL+xIXwI=">
220
+ console.log("nonced!")
221
+ </script>
222
+ ```
223
+
224
+ #### Hash
213
225
 
214
- **You need to add the following line to the TOP of confib/routes.rb**
215
- **This is an unauthenticated, unauthorized endpoint. Only do this if your report\-uri is not on the same origin as your application!!!**
226
+ 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.
216
227
 
217
- #### Rails 2
228
+ 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.
229
+
230
+ If you only have a few hashes, you can hardcode them for the entire app:
218
231
 
219
232
  ```ruby
220
- map.csp_endpoint
233
+ config.csp = {
234
+ :default_src => "https:",
235
+ :script_src => 'self'
236
+ :script_hashes => ['sha1-abc', 'sha1-qwe']
237
+ }
221
238
  ```
222
239
 
223
- #### Rails 3
240
+ The following will work as well, but may not be as clear:
224
241
 
225
- If the csp reporting endpoint is clobbered by another route, add:
242
+ ```ruby
243
+ config.csp = {
244
+ :default_src => "https:",
245
+ :script_src => "self 'sha1-qwe'"
246
+ }
247
+ ```
248
+
249
+ 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:
250
+
251
+ ```ruby
252
+ # config.ru
253
+ require 'secure_headers/headers/content_security_policy/script_hash_middleware'
254
+ use ::SecureHeaders::ContentSecurityPolicy::ScriptHashMiddleware
255
+ ```
226
256
 
227
257
  ```ruby
228
- post SecureHeaders::ContentSecurityPolicy::FF_CSP_ENDPOINT => "content_security_policy#scribe"
258
+ config.csp = {
259
+ :default_src => "https:",
260
+ :script_src => 'self',
261
+ :script_hash_middleware => true
262
+ }
263
+ ```
264
+
265
+ 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:
266
+
267
+ ```$ bundle exec rake secure_headers:generate_hashes```
268
+
269
+ Which will generate something like:
270
+
271
+ ```yaml
272
+ # config/script_hashes.yml
273
+ app/views/layouts/application.html.erb:
274
+ - sha256-l8OLjZqYRnKilpdE0VosRMvhdYArjXT4NZaK2p7QVvs=
275
+ app/templates/articles/edit.html.erb:
276
+ - sha256-+7mij1/uCwtCQRWrof2NmOln5qX+5WdVwTLMpi8nuoA=
277
+ - sha256-Ny4TRIhhFpnYnSeKC274P6bfAz4TOkezLabavIAU4dA=
278
+ - sha256-I5e58Gqbu4WpO9dck18QxO7aYOHKrELIi70it4jIPi0=
279
+ - sha256-Po4LMynwnAJHxiTp3DQaQ3YDBj3paN/xrDoKl4OyxY4=
280
+ ```
281
+
282
+ In this example, if we visit /articles/edit/[id], the above hashes will automatically be added to the CSP header's
283
+ script-src value!
284
+
285
+ You can use plain "script" tags or you can use a built-in helper:
286
+
287
+ ```erb
288
+ <%= hashed_javascript_tag do %>
289
+ console.log("hashed automatically!")
290
+ <% end %>
291
+ ```
292
+
293
+ 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:
294
+
295
+ ```erb
296
+ <%= hashed_javascript_tag(raise_error_on_unrecognized_hash = true) do %>
297
+ console.log("will raise an exception if not in script_hashes.yml!")
298
+ <% end %>
229
299
  ```
300
+
230
301
  ### Using with Sinatra
231
302
 
232
303
  Here's an example using SecureHeaders for Sinatra applications:
@@ -242,11 +313,13 @@ require 'secure_headers'
242
313
  config.x_frame_options = 'DENY'
243
314
  config.x_content_type_options = "nosniff"
244
315
  config.x_xss_protection = {:value => 1, :mode => false}
316
+ config.x_download_options = 'noopen'
317
+ config.x_permitted_cross_domain_policies = 'none'
245
318
  config.csp = {
246
- :default_src => "https://* inline eval",
319
+ :default_src => "https: inline eval",
247
320
  :report_uri => '//example.com/uri-directive',
248
- :img_src => "https://* data:",
249
- :frame_src => "https://* http://*.twimg.com http://itunes.apple.com"
321
+ :img_src => "https: data:",
322
+ :frame_src => "https: http:.twimg.com http://itunes.apple.com"
250
323
  }
251
324
  end
252
325
 
@@ -274,22 +347,11 @@ In your `Gemfile`:
274
347
  then in your `app.rb` file you can:
275
348
 
276
349
  ```ruby
350
+ require 'secure_headers/padrino'
351
+
277
352
  module Web
278
353
  class App < Padrino::Application
279
- include SecureHeaders
280
-
281
- ::SecureHeaders::Configuration.configure do |config|
282
- config.hsts = {:max_age => 99, :include_subdomains => true}
283
- config.x_frame_options = 'DENY'
284
- config.x_content_type_options = "nosniff"
285
- config.x_xss_protection = {:value => '1', :mode => false}
286
- config.csp = {
287
- :default_src => "https://* inline eval",
288
- :report_uri => '//example.com/uri-directive',
289
- :img_src => "https://* data:",
290
- :frame_src => "https://* http://*.twimg.com http://itunes.apple.com"
291
- }
292
- end
354
+ register SecureHeaders::Padrino
293
355
 
294
356
  get '/' do
295
357
  set_csp_header
@@ -299,12 +361,33 @@ module Web
299
361
  end
300
362
  ```
301
363
 
364
+ and in `config/boot.rb`:
365
+
366
+ ```ruby
367
+ def before_load
368
+ ::SecureHeaders::Configuration.configure do |config|
369
+ config.hsts = {:max_age => 99, :include_subdomains => true}
370
+ config.x_frame_options = 'DENY'
371
+ config.x_content_type_options = "nosniff"
372
+ config.x_xss_protection = {:value => '1', :mode => false}
373
+ config.x_download_options = 'noopen'
374
+ config.x_permitted_cross_domain_policies = 'none'
375
+ config.csp = {
376
+ :default_src => "https: inline eval",
377
+ :report_uri => '//example.com/uri-directive',
378
+ :img_src => "https: data:",
379
+ :frame_src => "https: http:.twimg.com http://itunes.apple.com"
380
+ }
381
+ end
382
+ end
383
+ ```
384
+
302
385
  ## Similar libraries
303
386
 
304
387
  * Node.js (express) [helmet](https://github.com/evilpacket/helmet) and [hood](https://github.com/seanmonstar/hood)
305
388
  * J2EE Servlet >= 3.0 [highlines](https://github.com/sourceclear/headlines)
306
389
  * ASP.NET - [NWebsec](http://nwebsec.codeplex.com/)
307
- * Python - [django-csp](https://github.com/mozilla/django-csp/tree/master/csp) + [commonware](https://github.com/jsocol/commonware/tree/master/commonware/request)
390
+ * Python - [django-csp](https://github.com/mozilla/django-csp/) + [commonware](https://github.com/jsocol/commonware/)
308
391
  * Go - [secureheader](https://github.com/kr/secureheader)
309
392
 
310
393
  ## Authors
@@ -321,6 +404,6 @@ end
321
404
 
322
405
  ## License
323
406
 
324
- Copyright 2013 Twitter, Inc.
407
+ Copyright 2013-2014 Twitter, Inc and other contributors.
325
408
 
326
409
  Licensed under the Apache License, Version 2.0: http://www.apache.org/licenses/LICENSE-2.0
data/Rakefile CHANGED
@@ -33,6 +33,17 @@ task :all_spec => :spec do
33
33
  fail "Header tests with app not using initializer failed"
34
34
  Dir.chdir pwd
35
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
+
36
47
  end
37
48
 
38
49
  begin
@@ -49,119 +60,3 @@ RDoc::Task.new(:rdoc) do |rdoc|
49
60
  rdoc.options << '--line-numbers'
50
61
  rdoc.rdoc_files.include('lib/**/*.rb')
51
62
  end
52
-
53
- UPDATE_URI = 'https://mxr.mozilla.org/mozilla/source/security/nss/lib/ckfw/builtins/certdata.txt?raw=1'
54
- CA_FILE = File.expand_path(File.join('..', 'config', 'curl-ca-bundle.crt'), __FILE__)
55
- task :fetch_ca_bundle do
56
- begin
57
- FileUtils.cp CA_FILE, CA_FILE + ".bak"
58
- uri = URI.parse(UPDATE_URI)
59
- http = Net::HTTP.new(uri.host, uri.port)
60
- http.use_ssl = true
61
- http.ca_file = CA_FILE
62
- http.verify_mode = OpenSSL::SSL::VERIFY_PEER
63
- request = Net::HTTP::Get.new(uri.request_uri)
64
-
65
- ca_file = StringIO.new(http.request(request).body)
66
- File.open(CA_FILE, 'w') do |f|
67
- f.puts mozilla_license
68
- end
69
-
70
- while line = ca_file.gets
71
- next if line =~ /^#/
72
- next if line =~ /^\s*$/
73
- line.chomp!
74
-
75
- if line =~ /CKA_LABEL/
76
- label,type,cert_name = line.split(' ',3)
77
- cert_name.sub!(/^"/, "")
78
- cert_name.sub!(/"$/, "")
79
- next
80
- end
81
- if line =~ /CKA_VALUE MULTILINE_OCTAL/
82
- puts "reading cert for #{cert_name}"
83
- data=''
84
- while line = ca_file.gets
85
- break if line =~ /^END/
86
- line.chomp!
87
- line.gsub(/\\([0-3][0-7][0-7])/) { data += $1.oct.chr }
88
- end
89
-
90
- open(CA_FILE, "a") do |fp|
91
- puts "Appending"
92
- fp.puts cert_name
93
- fp.puts "================"
94
- fp.puts "-----BEGIN CERTIFICATE-----"
95
- fp.puts [data].pack("m*")
96
- fp.puts "-----END CERTIFICATE-----"
97
- fp.puts
98
- end
99
- puts "Parsing: " + cert_name
100
- end
101
- end
102
-
103
- FileUtils.rm CA_FILE + ".bak"
104
- rescue => e
105
- puts "ERRROR #{e}"
106
- puts e.backtrace
107
- FileUtils.mv CA_FILE + '.bak', CA_FILE
108
- end
109
- end
110
-
111
-
112
- def mozilla_license
113
- <<-EOM
114
- ## generated using a modified version of http://curl.haxx.se/mail/lib-2004-07/att-0134/parse-certs.sh
115
- ##
116
- ## lib/ca-bundle.crt -- Bundle of CA Root Certificates
117
- ##
118
- ## Certificate data from Mozilla as of: Tue Mar 27 20:21:58 2012
119
- ##
120
- ## This is a bundle of X.509 certificates of public Certificate Authorities
121
- ## (CA). These were automatically extracted from Mozilla's root certificates
122
- ## file (certdata.txt). This file can be found in the mozilla source tree:
123
- ## http://mxr.mozilla.org/mozilla/source/security/nss/lib/ckfw/builtins/certdata.txt?raw=1
124
- ##
125
- ## It contains the certificates in PEM format and therefore
126
- ## can be directly used with curl / libcurl / php_curl, or with
127
- ## an Apache+mod_ssl webserver for SSL client authentication.
128
- ## Just configure this file as the SSLCACertificateFile.
129
- ##
130
-
131
- # ***** BEGIN LICENSE BLOCK *****
132
- # Version: MPL 1.1/GPL 2.0/LGPL 2.1
133
- #
134
- # The contents of this file are subject to the Mozilla Public License Version
135
- # 1.1 (the "License"); you may not use this file except in compliance with
136
- # the License. You may obtain a copy of the License at
137
- # http://www.mozilla.org/MPL/
138
- #
139
- # Software distributed under the License is distributed on an "AS IS" basis,
140
- # WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
141
- # for the specific language governing rights and limitations under the
142
- # License.
143
- #
144
- # The Original Code is the Netscape security libraries.
145
- #
146
- # The Initial Developer of the Original Code is
147
- # Netscape Communications Corporation.
148
- # Portions created by the Initial Developer are Copyright (C) 1994-2000
149
- # the Initial Developer. All Rights Reserved.
150
- #
151
- # Contributor(s):
152
- #
153
- # Alternatively, the contents of this file may be used under the terms of
154
- # either the GNU General Public License Version 2 or later (the "GPL"), or
155
- # the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
156
- # in which case the provisions of the GPL or the LGPL are applicable instead
157
- # of those above. If you wish to allow use of your version of this file only
158
- # under the terms of either the GPL or the LGPL, and not to allow others to
159
- # use your version of this file under the terms of the MPL, indicate your
160
- # decision by deleting the provisions above and replace them with the notice
161
- # and other provisions required by the GPL or the LGPL. If you do not delete
162
- # the provisions above, a recipient may use your version of this file under
163
- # the terms of any one of the MPL, the GPL or the LGPL.
164
- #
165
- # ***** END LICENSE BLOCK *****
166
- EOM
167
- end
@@ -1,14 +1,6 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
3
  gem 'rails', '3.2.12'
4
- gem 'sqlite3'
5
4
  gem 'rspec-rails', '>= 2.0.0'
6
5
  gem 'secure_headers', :path => '../..'
7
- gem 'spork'
8
- gem 'debugger', :platform => :ruby_19
9
- gem 'ruby-debug', :platform => :ruby_18
10
- gem 'guard-rspec'
11
- gem 'guard-spork'
12
- gem 'rb-fsevent'
13
- gem 'growl'
14
6
 
@@ -1,6 +1,5 @@
1
1
  class ThingsController < ApplicationController
2
2
  ensure_security_headers :csp => false
3
3
  def index
4
- ######## : ) <- Marge Simpson?
5
4
  end
6
5
  end
@@ -2,13 +2,10 @@
2
2
  <html>
3
3
  <head>
4
4
  <title>Rails3212</title>
5
- <%= stylesheet_link_tag "application", :media => "all" %>
6
- <%= javascript_include_tag "application" %>
7
- <%= csrf_meta_tags %>
8
5
  </head>
9
6
  <body>
10
7
 
11
8
  <%= yield %>
12
-
9
+ <script>console.log("oh hell nah")</script>
13
10
  </body>
14
11
  </html>
@@ -1 +1,2 @@
1
- index
1
+ index
2
+ <script>console.log("oh what")</script>