secure_headers 3.2.0 → 3.3.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.

Potentially problematic release.


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

checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a02f16867ec55f8c168ace664cc63d760785256f
4
- data.tar.gz: db54c113af8919984c8f382b957834f38210cf3e
3
+ metadata.gz: c3acd5dfb3fc3a7c4037ef42b72765a4f1b34ede
4
+ data.tar.gz: 98f0ce842bac69bf7c5f4d3d4cc19e4281ebe00e
5
5
  SHA512:
6
- metadata.gz: 6d9ddcb98a8c646d4e7a8cdb79bf3d8638cae4cb230ff268660e8eab7000fba99bb65e126ce55b3ff5f2a0346013fcf8b769dab4db697c82ab4461d444d0e2bd
7
- data.tar.gz: b63c79ac3e9b37a4cff698f979f1ca6fd7e92567e35b1f9026be2e68a205e5badff302d512938a4d36517e8ec5b752d4833082e1b733ff73916fbf0b326641aa
6
+ metadata.gz: 75d3b9033b09f0ac40675e8e8706185d9c0ca465a38f857228bf56857f5f44ba1eab5559b9c9f64f34ee398226ccd9dda5e26923f5325dcadd7be8ff0a957314
7
+ data.tar.gz: f6ffb2babf72009e60a4d4663ee6e933db8f8acb7159021e66a07168e1c6743df621309fb63e884f3257217d28951b196b2a192739860ce459f4fbabbab35622
data/CHANGELOG.md CHANGED
@@ -1,3 +1,11 @@
1
+ ## 3.3.0 referrer-policy support
2
+
3
+ While not officially part of the spec and not implemented anywhere, support for the experimental [`referrer-policy` header](https://w3c.github.io/webappsec-referrer-policy/#referrer-policy-header) was [preemptively added](https://github.com/twitter/secureheaders/pull/249).
4
+
5
+ Additionally, two minor enhancements were added this version:
6
+ 1. [Warn when the HPKP report host is the same as the current host](https://github.com/twitter/secureheaders/pull/246). By definition any generated reports would be reporting to a known compromised connection.
7
+ 1. [Filter unsupported CSP directives when using Edge](https://github.com/twitter/secureheaders/pull/247). Previously, this was causing many warnings in the developer console.
8
+
1
9
  ## 3.2.0 Cookie settings and CSP hash sources
2
10
 
3
11
  ### Cookies
@@ -114,6 +122,7 @@ console.log(1)
114
122
  Content-Security-Policy: ...
115
123
  script-src 'sha256-yktKiAsZWmc8WpOyhnmhQoDf9G2dAZvuBBC+V0LGQhg=' ... ;
116
124
  style-src 'sha256-SLp6LO3rrKDJwsG9uJUxZapb4Wp2Zhj6Bu3l+d9rnAY=' 'sha256-HSGHqlRoKmHAGTAJ2Rq0piXX4CnEbOl1ArNd6ejp2TE=' ...;
125
+ ```
117
126
 
118
127
  ## 3.1.2 Bug fix for regression
119
128
 
data/README.md CHANGED
@@ -13,6 +13,7 @@ The gem will automatically apply several headers that are related to security.
13
13
  - X-Content-Type-Options - [Prevent content type sniffing](https://msdn.microsoft.com/library/gg622941\(v=vs.85\).aspx)
14
14
  - X-Download-Options - [Prevent file downloads opening](https://msdn.microsoft.com/library/jj542450(v=vs.85).aspx)
15
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)
16
+ - Referrer-Policy - [Referrer Policy draft](https://w3c.github.io/webappsec-referrer-policy/)
16
17
  - 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)
17
18
 
18
19
  It can also mark all http cookies with the Secure, HttpOnly and SameSite attributes (when configured to do so).
@@ -44,6 +45,7 @@ SecureHeaders::Configuration.default do |config|
44
45
  config.x_xss_protection = "1; mode=block"
45
46
  config.x_download_options = "noopen"
46
47
  config.x_permitted_cross_domain_policies = "none"
48
+ config.referrer_policy = "origin-when-cross-origin"
47
49
  config.csp = {
48
50
  # "meta" values. these will shaped the header, but the values are not included in the header.
49
51
  report_only: true, # default: false
@@ -330,9 +332,7 @@ config.hpkp = {
330
332
  {sha256: '73a2c64f9545172c1195efb6616ca5f7afd1df6f245407cafb90de3998a1c97f'}
331
333
  ],
332
334
  report_only: true, # defaults to false (report-only mode)
333
- report_uri: 'https://report-uri.io/example-hpkp',
334
- app_name: 'example',
335
- tag_report_uri: true
335
+ report_uri: 'https://report-uri.io/example-hpkp'
336
336
  }
337
337
  ```
338
338
 
@@ -61,6 +61,7 @@ module SecureHeaders
61
61
  config.validate_config!
62
62
  @configurations ||= {}
63
63
  config.send(:cache_headers!)
64
+ config.send(:cache_hpkp_report_host)
64
65
  config.freeze
65
66
  @configurations[name] = config
66
67
  end
@@ -102,11 +103,13 @@ module SecureHeaders
102
103
  end
103
104
  end
104
105
 
106
+ attr_accessor :dynamic_csp
107
+
105
108
  attr_writer :hsts, :x_frame_options, :x_content_type_options,
106
109
  :x_xss_protection, :x_download_options, :x_permitted_cross_domain_policies,
107
- :hpkp, :dynamic_csp, :cookies
110
+ :referrer_policy
108
111
 
109
- attr_reader :cached_headers, :csp, :dynamic_csp, :cookies
112
+ attr_reader :cached_headers, :csp, :cookies, :hpkp, :hpkp_report_host
110
113
 
111
114
  HASH_CONFIG_FILE = ENV["secure_headers_generated_hashes_file"] || "config/secure_headers_generated_hashes.yml"
112
115
  if File.exists?(HASH_CONFIG_FILE)
@@ -117,6 +120,7 @@ module SecureHeaders
117
120
 
118
121
  def initialize(&block)
119
122
  self.hpkp = OPT_OUT
123
+ self.referrer_policy = OPT_OUT
120
124
  self.csp = self.class.send(:deep_copy, CSP::DEFAULT_CONFIG)
121
125
  instance_eval &block if block_given?
122
126
  end
@@ -136,7 +140,9 @@ module SecureHeaders
136
140
  copy.x_xss_protection = @x_xss_protection
137
141
  copy.x_download_options = @x_download_options
138
142
  copy.x_permitted_cross_domain_policies = @x_permitted_cross_domain_policies
143
+ copy.referrer_policy = @referrer_policy
139
144
  copy.hpkp = @hpkp
145
+ copy.hpkp_report_host = @hpkp_report_host
140
146
  copy
141
147
  end
142
148
 
@@ -175,6 +181,7 @@ module SecureHeaders
175
181
  def validate_config!
176
182
  StrictTransportSecurity.validate_config!(@hsts)
177
183
  ContentSecurityPolicy.validate_config!(@csp)
184
+ ReferrerPolicy.validate_config!(@referrer_policy)
178
185
  XFrameOptions.validate_config!(@x_frame_options)
179
186
  XContentTypeOptions.validate_config!(@x_content_type_options)
180
187
  XXssProtection.validate_config!(@x_xss_protection)
@@ -199,12 +206,32 @@ module SecureHeaders
199
206
  @csp = new_csp
200
207
  end
201
208
 
209
+ def cookies=(cookies)
210
+ @cookies = cookies
211
+ end
212
+
202
213
  def cached_headers=(headers)
203
214
  @cached_headers = headers
204
215
  end
205
216
 
217
+ def hpkp=(hpkp)
218
+ @hpkp = hpkp
219
+ end
220
+
221
+ def hpkp_report_host=(hpkp_report_host)
222
+ @hpkp_report_host = hpkp_report_host
223
+ end
224
+
206
225
  private
207
226
 
227
+ def cache_hpkp_report_host
228
+ has_report_uri = @hpkp && @hpkp != OPT_OUT && @hpkp[:report_uri]
229
+ self.hpkp_report_host = if has_report_uri
230
+ parsed_report_uri = URI.parse(@hpkp[:report_uri])
231
+ parsed_report_uri.host
232
+ end
233
+ end
234
+
208
235
  # Public: Precompute the header names and values for this configuraiton.
209
236
  # Ensures that headers generated at configure time, not on demand.
210
237
  #
@@ -91,6 +91,7 @@ module SecureHeaders
91
91
  UPGRADE_INSECURE_REQUESTS
92
92
  ].freeze
93
93
 
94
+ EDGE_DIRECTIVES = DIRECTIVES_1_0
94
95
  SAFARI_DIRECTIVES = DIRECTIVES_1_0
95
96
 
96
97
  FIREFOX_UNSUPPORTED_DIRECTIVES = [
@@ -118,6 +119,7 @@ module SecureHeaders
118
119
  "Opera" => CHROME_DIRECTIVES,
119
120
  "Firefox" => FIREFOX_DIRECTIVES,
120
121
  "Safari" => SAFARI_DIRECTIVES,
122
+ "Edge" => EDGE_DIRECTIVES,
121
123
  "Other" => CHROME_DIRECTIVES
122
124
  }.freeze
123
125
 
@@ -0,0 +1,33 @@
1
+ module SecureHeaders
2
+ class ReferrerPolicyConfigError < StandardError; end
3
+ class ReferrerPolicy
4
+ HEADER_NAME = "Referrer-Policy"
5
+ DEFAULT_VALUE = "origin-when-cross-origin"
6
+ VALID_POLICIES = %w(
7
+ no-referrer
8
+ no-referrer-when-downgrade
9
+ origin
10
+ origin-when-cross-origin
11
+ unsafe-url
12
+ )
13
+ CONFIG_KEY = :referrer_policy
14
+
15
+ class << self
16
+ # Public: generate an Referrer Policy header.
17
+ #
18
+ # Returns a default header if no configuration is provided, or a
19
+ # header name and value based on the config.
20
+ def make_header(config = nil)
21
+ [HEADER_NAME, config || DEFAULT_VALUE]
22
+ end
23
+
24
+ def validate_config!(config)
25
+ return if config.nil? || config == OPT_OUT
26
+ raise TypeError.new("Must be a string. Found #{config.class}: #{config}") unless config.is_a?(String)
27
+ unless VALID_POLICIES.include?(config.downcase)
28
+ raise ReferrerPolicyConfigError.new("Value can only be one of #{VALID_POLICIES.join(', ')}")
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -1,5 +1,7 @@
1
1
  module SecureHeaders
2
2
  class Middleware
3
+ HPKP_SAME_HOST_WARNING = "[WARNING] HPKP report host should not be the same as the request host. See https://github.com/twitter/secureheaders/issues/166"
4
+
3
5
  def initialize(app)
4
6
  @app = app
5
7
  end
@@ -10,6 +12,10 @@ module SecureHeaders
10
12
  status, headers, response = @app.call(env)
11
13
 
12
14
  config = SecureHeaders.config_for(req)
15
+ if config.hpkp_report_host == req.host
16
+ Kernel.warn(HPKP_SAME_HOST_WARNING)
17
+ end
18
+
13
19
  flag_cookies!(headers, override_secure(env, config.cookies)) if config.cookies
14
20
  headers.merge!(SecureHeaders.header_hash_for(req))
15
21
  [status, headers, response]
@@ -7,7 +7,7 @@ if defined?(Rails::Railtie)
7
7
  'X-Permitted-Cross-Domain-Policies', 'X-Download-Options',
8
8
  'X-Content-Type-Options', 'Strict-Transport-Security',
9
9
  'Content-Security-Policy', 'Content-Security-Policy-Report-Only',
10
- 'Public-Key-Pins', 'Public-Key-Pins-Report-Only']
10
+ 'Public-Key-Pins', 'Public-Key-Pins-Report-Only', 'Referrer-Policy']
11
11
 
12
12
  initializer "secure_headers.middleware" do
13
13
  Rails.application.config.middleware.insert_before 0, SecureHeaders::Middleware
@@ -9,6 +9,7 @@ require "secure_headers/headers/x_xss_protection"
9
9
  require "secure_headers/headers/x_content_type_options"
10
10
  require "secure_headers/headers/x_download_options"
11
11
  require "secure_headers/headers/x_permitted_cross_domain_policies"
12
+ require "secure_headers/headers/referrer_policy"
12
13
  require "secure_headers/middleware"
13
14
  require "secure_headers/railtie"
14
15
  require "secure_headers/view_helper"
@@ -27,6 +28,7 @@ module SecureHeaders
27
28
  ContentSecurityPolicy,
28
29
  StrictTransportSecurity,
29
30
  PublicKeyPins,
31
+ ReferrerPolicy,
30
32
  XContentTypeOptions,
31
33
  XDownloadOptions,
32
34
  XFrameOptions,
@@ -1,7 +1,7 @@
1
1
  # -*- encoding: utf-8 -*-
2
2
  Gem::Specification.new do |gem|
3
3
  gem.name = "secure_headers"
4
- gem.version = "3.2.0"
4
+ gem.version = "3.3.0"
5
5
  gem.authors = ["Neil Matatall"]
6
6
  gem.email = ["neil.matatall@gmail.com"]
7
7
  gem.description = 'Security related headers all in one gem.'
@@ -104,6 +104,11 @@ module SecureHeaders
104
104
  expect(policy.value).to eq("default-src 'self'; base-uri 'self'; connect-src 'self'; font-src 'self'; form-action 'self'; frame-ancestors 'self'; frame-src 'self'; img-src 'self'; media-src 'self'; object-src 'self'; sandbox 'self'; script-src 'self' 'nonce-123456'; style-src 'self'; upgrade-insecure-requests; report-uri 'self'")
105
105
  end
106
106
 
107
+ it "adds 'unsafe-inline', filters base-uri, blocked-all-mixed-content, upgrade-insecure-requests, child-src, form-action, frame-ancestors, nonce sources, hash sources, and plugin-types for Edge" do
108
+ policy = ContentSecurityPolicy.new(complex_opts, USER_AGENTS[:edge])
109
+ expect(policy.value).to eq("default-src 'self'; connect-src 'self'; font-src 'self'; frame-src 'self'; img-src 'self'; media-src 'self'; object-src 'self'; sandbox 'self'; script-src 'self' 'unsafe-inline'; style-src 'self'; report-uri 'self'")
110
+ end
111
+
107
112
  it "adds 'unsafe-inline', filters base-uri, blocked-all-mixed-content, upgrade-insecure-requests, child-src, form-action, frame-ancestors, nonce sources, hash sources, and plugin-types for safari" do
108
113
  policy = ContentSecurityPolicy.new(complex_opts, USER_AGENTS[:safari6])
109
114
  expect(policy.value).to eq("default-src 'self'; connect-src 'self'; font-src 'self'; frame-src 'self'; img-src 'self'; media-src 'self'; object-src 'self'; sandbox 'self'; script-src 'self' 'unsafe-inline'; style-src 'self'; report-uri 'self'")
@@ -0,0 +1,54 @@
1
+ require 'spec_helper'
2
+
3
+ module SecureHeaders
4
+ describe ReferrerPolicy do
5
+ specify { expect(ReferrerPolicy.make_header).to eq([ReferrerPolicy::HEADER_NAME, "origin-when-cross-origin"]) }
6
+ specify { expect(ReferrerPolicy.make_header('no-referrer')).to eq([ReferrerPolicy::HEADER_NAME, "no-referrer"]) }
7
+
8
+ context "valid configuration values" do
9
+ it "accepts 'no-referrer'" do
10
+ expect do
11
+ ReferrerPolicy.validate_config!("no-referrer")
12
+ end.not_to raise_error
13
+ end
14
+
15
+ it "accepts 'no-referrer-when-downgrade'" do
16
+ expect do
17
+ ReferrerPolicy.validate_config!("no-referrer-when-downgrade")
18
+ end.not_to raise_error
19
+ end
20
+
21
+ it "accepts 'origin'" do
22
+ expect do
23
+ ReferrerPolicy.validate_config!("origin")
24
+ end.not_to raise_error
25
+ end
26
+
27
+ it "accepts 'origin-when-cross-origin'" do
28
+ expect do
29
+ ReferrerPolicy.validate_config!("origin-when-cross-origin")
30
+ end.not_to raise_error
31
+ end
32
+
33
+ it "accepts 'unsafe-url'" do
34
+ expect do
35
+ ReferrerPolicy.validate_config!("unsafe-url")
36
+ end.not_to raise_error
37
+ end
38
+
39
+ it "accepts nil" do
40
+ expect do
41
+ ReferrerPolicy.validate_config!(nil)
42
+ end.not_to raise_error
43
+ end
44
+ end
45
+
46
+ context 'invlaid configuration values' do
47
+ it "doesn't accept invalid values" do
48
+ expect do
49
+ ReferrerPolicy.validate_config!("open")
50
+ end.to raise_error(ReferrerPolicyConfigError)
51
+ end
52
+ end
53
+ end
54
+ end
@@ -13,6 +13,24 @@ module SecureHeaders
13
13
  Configuration.default
14
14
  end
15
15
 
16
+ it "warns if the hpkp report-uri host is the same as the current host" do
17
+ report_host = "report-uri.io"
18
+ Configuration.default do |config|
19
+ config.hpkp = {
20
+ max_age: 10000000,
21
+ pins: [
22
+ {sha256: 'b5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b878ae4944c'},
23
+ {sha256: '73a2c64f9545172c1195efb6616ca5f7afd1df6f245407cafb90de3998a1c97f'}
24
+ ],
25
+ report_uri: "https://#{report_host}/example-hpkp"
26
+ }
27
+ end
28
+
29
+ expect(Kernel).to receive(:warn).with(Middleware::HPKP_SAME_HOST_WARNING)
30
+
31
+ middleware.call(Rack::MockRequest.env_for("https://#{report_host}", {}))
32
+ end
33
+
16
34
  it "sets the headers" do
17
35
  _, env = middleware.call(Rack::MockRequest.env_for("https://looocalhost", {}))
18
36
  expect_default_values(env)
@@ -20,6 +20,13 @@ module SecureHeaders
20
20
  end.to raise_error(Configuration::NotYetConfiguredError)
21
21
  end
22
22
 
23
+ it "raises and ArgumentError when referencing an override that has not been set" do
24
+ expect do
25
+ Configuration.default
26
+ SecureHeaders.use_secure_headers_override(request, :missing)
27
+ end.to raise_error(ArgumentError)
28
+ end
29
+
23
30
  describe "#header_hash_for" do
24
31
  it "allows you to opt out of individual headers via API" do
25
32
  Configuration.default
@@ -305,6 +312,14 @@ module SecureHeaders
305
312
  end.to raise_error(XPCDPConfigError)
306
313
  end
307
314
 
315
+ it "validates your referrer_policy config upon configuration" do
316
+ expect do
317
+ Configuration.default do |config|
318
+ config.referrer_policy = "lol"
319
+ end
320
+ end.to raise_error(ReferrerPolicyConfigError)
321
+ end
322
+
308
323
  it "validates your hpkp config upon configuration" do
309
324
  expect do
310
325
  Configuration.default do |config|
@@ -312,6 +327,14 @@ module SecureHeaders
312
327
  end
313
328
  end.to raise_error(PublicKeyPinsConfigError)
314
329
  end
330
+
331
+ it "validates your cookies config upon configuration" do
332
+ expect do
333
+ Configuration.default do |config|
334
+ config.cookies = { secure: "lol" }
335
+ end
336
+ end.to raise_error(CookiesConfigError)
337
+ end
315
338
  end
316
339
  end
317
340
  end
data/spec/spec_helper.rb CHANGED
@@ -11,6 +11,7 @@ require File.join(File.dirname(__FILE__), '..', 'lib', 'secure_headers')
11
11
 
12
12
 
13
13
  USER_AGENTS = {
14
+ edge: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.135 Safari/537.36 Edge/12.246",
14
15
  firefox: 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:14.0) Gecko/20100101 Firefox/14.0.1',
15
16
  chrome: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_8) AppleWebKit/536.5 (KHTML, like Gecko) Chrome/19.0.1084.56 Safari/536.5',
16
17
  ie: 'Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; Trident/5.0)',
@@ -30,6 +31,7 @@ def expect_default_values(hash)
30
31
  expect(hash[SecureHeaders::XXssProtection::HEADER_NAME]).to eq(SecureHeaders::XXssProtection::DEFAULT_VALUE)
31
32
  expect(hash[SecureHeaders::XContentTypeOptions::HEADER_NAME]).to eq(SecureHeaders::XContentTypeOptions::DEFAULT_VALUE)
32
33
  expect(hash[SecureHeaders::XPermittedCrossDomainPolicies::HEADER_NAME]).to eq(SecureHeaders::XPermittedCrossDomainPolicies::DEFAULT_VALUE)
34
+ expect(hash[SecureHeaders::ReferrerPolicy::HEADER_NAME]).to be_nil
33
35
  end
34
36
 
35
37
  module SecureHeaders
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: secure_headers
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.2.0
4
+ version: 3.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Neil Matatall
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-04-18 00:00:00.000000000 Z
11
+ date: 2016-04-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
@@ -63,6 +63,7 @@ files:
63
63
  - lib/secure_headers/headers/cookie.rb
64
64
  - lib/secure_headers/headers/policy_management.rb
65
65
  - lib/secure_headers/headers/public_key_pins.rb
66
+ - lib/secure_headers/headers/referrer_policy.rb
66
67
  - lib/secure_headers/headers/strict_transport_security.rb
67
68
  - lib/secure_headers/headers/x_content_type_options.rb
68
69
  - lib/secure_headers/headers/x_download_options.rb
@@ -80,6 +81,7 @@ files:
80
81
  - spec/lib/secure_headers/headers/cookie_spec.rb
81
82
  - spec/lib/secure_headers/headers/policy_management_spec.rb
82
83
  - spec/lib/secure_headers/headers/public_key_pins_spec.rb
84
+ - spec/lib/secure_headers/headers/referrer_policy_spec.rb
83
85
  - spec/lib/secure_headers/headers/strict_transport_security_spec.rb
84
86
  - spec/lib/secure_headers/headers/x_content_type_options_spec.rb
85
87
  - spec/lib/secure_headers/headers/x_download_options_spec.rb
@@ -122,6 +124,7 @@ test_files:
122
124
  - spec/lib/secure_headers/headers/cookie_spec.rb
123
125
  - spec/lib/secure_headers/headers/policy_management_spec.rb
124
126
  - spec/lib/secure_headers/headers/public_key_pins_spec.rb
127
+ - spec/lib/secure_headers/headers/referrer_policy_spec.rb
125
128
  - spec/lib/secure_headers/headers/strict_transport_security_spec.rb
126
129
  - spec/lib/secure_headers/headers/x_content_type_options_spec.rb
127
130
  - spec/lib/secure_headers/headers/x_download_options_spec.rb