Skip to content

Commit 681c8af

Browse files
committed
Merge branch 'master' into hpkp-warn
2 parents ef2d123 + d3aac03 commit 681c8af

File tree

10 files changed

+129
-2
lines changed

10 files changed

+129
-2
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ The gem will automatically apply several headers that are related to security.
1313
- X-Content-Type-Options - [Prevent content type sniffing](https://msdn.microsoft.com/library/gg622941\(v=vs.85\).aspx)
1414
- X-Download-Options - [Prevent file downloads opening](https://msdn.microsoft.com/library/jj542450(v=vs.85).aspx)
1515
- 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/)
1617
- 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)
1718

1819
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|
4445
config.x_xss_protection = "1; mode=block"
4546
config.x_download_options = "noopen"
4647
config.x_permitted_cross_domain_policies = "none"
48+
config.referrer_policy = "origin-when-cross-origin"
4749
config.csp = {
4850
# "meta" values. these will shaped the header, but the values are not included in the header.
4951
report_only: true, # default: false

lib/secure_headers.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
require "secure_headers/headers/x_content_type_options"
1010
require "secure_headers/headers/x_download_options"
1111
require "secure_headers/headers/x_permitted_cross_domain_policies"
12+
require "secure_headers/headers/referrer_policy"
1213
require "secure_headers/middleware"
1314
require "secure_headers/railtie"
1415
require "secure_headers/view_helper"
@@ -27,6 +28,7 @@ module SecureHeaders
2728
ContentSecurityPolicy,
2829
StrictTransportSecurity,
2930
PublicKeyPins,
31+
ReferrerPolicy,
3032
XContentTypeOptions,
3133
XDownloadOptions,
3234
XFrameOptions,

lib/secure_headers/configuration.rb

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,8 @@ def deep_copy_if_hash(value)
106106
attr_accessor :dynamic_csp
107107

108108
attr_writer :hsts, :x_frame_options, :x_content_type_options,
109-
:x_xss_protection, :x_download_options, :x_permitted_cross_domain_policies
109+
:x_xss_protection, :x_download_options, :x_permitted_cross_domain_policies,
110+
:referrer_policy
110111

111112
attr_reader :cached_headers, :csp, :cookies, :hpkp, :hpkp_report_host
112113

@@ -119,6 +120,7 @@ def deep_copy_if_hash(value)
119120

120121
def initialize(&block)
121122
self.hpkp = OPT_OUT
123+
self.referrer_policy = OPT_OUT
122124
self.csp = self.class.send(:deep_copy, CSP::DEFAULT_CONFIG)
123125
instance_eval &block if block_given?
124126
end
@@ -138,6 +140,7 @@ def dup
138140
copy.x_xss_protection = @x_xss_protection
139141
copy.x_download_options = @x_download_options
140142
copy.x_permitted_cross_domain_policies = @x_permitted_cross_domain_policies
143+
copy.referrer_policy = @referrer_policy
141144
copy.hpkp = @hpkp
142145
copy.hpkp_report_host = @hpkp_report_host
143146
copy
@@ -178,6 +181,7 @@ def current_csp
178181
def validate_config!
179182
StrictTransportSecurity.validate_config!(@hsts)
180183
ContentSecurityPolicy.validate_config!(@csp)
184+
ReferrerPolicy.validate_config!(@referrer_policy)
181185
XFrameOptions.validate_config!(@x_frame_options)
182186
XContentTypeOptions.validate_config!(@x_content_type_options)
183187
XXssProtection.validate_config!(@x_xss_protection)

lib/secure_headers/headers/policy_management.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ def self.included(base)
9191
UPGRADE_INSECURE_REQUESTS
9292
].freeze
9393

94+
EDGE_DIRECTIVES = DIRECTIVES_1_0
9495
SAFARI_DIRECTIVES = DIRECTIVES_1_0
9596

9697
FIREFOX_UNSUPPORTED_DIRECTIVES = [
@@ -118,6 +119,7 @@ def self.included(base)
118119
"Opera" => CHROME_DIRECTIVES,
119120
"Firefox" => FIREFOX_DIRECTIVES,
120121
"Safari" => SAFARI_DIRECTIVES,
122+
"Edge" => EDGE_DIRECTIVES,
121123
"Other" => CHROME_DIRECTIVES
122124
}.freeze
123125

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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

lib/secure_headers/railtie.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ class Railtie < Rails::Railtie
77
'X-Permitted-Cross-Domain-Policies', 'X-Download-Options',
88
'X-Content-Type-Options', 'Strict-Transport-Security',
99
'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']
1111

1212
initializer "secure_headers.middleware" do
1313
Rails.application.config.middleware.insert_before 0, SecureHeaders::Middleware

spec/lib/secure_headers/headers/content_security_policy_spec.rb

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,11 @@ module SecureHeaders
104104
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'")
105105
end
106106

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+
107112
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
108113
policy = ContentSecurityPolicy.new(complex_opts, USER_AGENTS[:safari6])
109114
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'")
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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

spec/lib/secure_headers_spec.rb

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,13 @@ module SecureHeaders
2020
end.to raise_error(Configuration::NotYetConfiguredError)
2121
end
2222

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+
2330
describe "#header_hash_for" do
2431
it "allows you to opt out of individual headers via API" do
2532
Configuration.default
@@ -305,13 +312,29 @@ module SecureHeaders
305312
end.to raise_error(XPCDPConfigError)
306313
end
307314

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+
308323
it "validates your hpkp config upon configuration" do
309324
expect do
310325
Configuration.default do |config|
311326
config.hpkp = "lol"
312327
end
313328
end.to raise_error(PublicKeyPinsConfigError)
314329
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
315338
end
316339
end
317340
end

spec/spec_helper.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212

1313
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",
1415
firefox: 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:14.0) Gecko/20100101 Firefox/14.0.1',
1516
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',
1617
ie: 'Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; Trident/5.0)',
@@ -30,6 +31,7 @@ def expect_default_values(hash)
3031
expect(hash[SecureHeaders::XXssProtection::HEADER_NAME]).to eq(SecureHeaders::XXssProtection::DEFAULT_VALUE)
3132
expect(hash[SecureHeaders::XContentTypeOptions::HEADER_NAME]).to eq(SecureHeaders::XContentTypeOptions::DEFAULT_VALUE)
3233
expect(hash[SecureHeaders::XPermittedCrossDomainPolicies::HEADER_NAME]).to eq(SecureHeaders::XPermittedCrossDomainPolicies::DEFAULT_VALUE)
34+
expect(hash[SecureHeaders::ReferrerPolicy::HEADER_NAME]).to be_nil
3335
end
3436

3537
module SecureHeaders

0 commit comments

Comments
 (0)