Skip to content

Commit a6f8066

Browse files
committed
Merge pull request #249 from tomgilligan/master
Add support for Referrer Policy header
2 parents ede35f9 + 07c9520 commit a6f8066

File tree

8 files changed

+105
-2
lines changed

8 files changed

+105
-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: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ def deep_copy_if_hash(value)
104104

105105
attr_writer :hsts, :x_frame_options, :x_content_type_options,
106106
:x_xss_protection, :x_download_options, :x_permitted_cross_domain_policies,
107-
:hpkp, :dynamic_csp, :cookies
107+
:hpkp, :dynamic_csp, :cookies, :referrer_policy
108108

109109
attr_reader :cached_headers, :csp, :dynamic_csp, :cookies
110110

@@ -117,6 +117,7 @@ def deep_copy_if_hash(value)
117117

118118
def initialize(&block)
119119
self.hpkp = OPT_OUT
120+
self.referrer_policy = OPT_OUT
120121
self.csp = self.class.send(:deep_copy, CSP::DEFAULT_CONFIG)
121122
instance_eval &block if block_given?
122123
end
@@ -136,6 +137,7 @@ def dup
136137
copy.x_xss_protection = @x_xss_protection
137138
copy.x_download_options = @x_download_options
138139
copy.x_permitted_cross_domain_policies = @x_permitted_cross_domain_policies
140+
copy.referrer_policy = @referrer_policy
139141
copy.hpkp = @hpkp
140142
copy
141143
end
@@ -175,6 +177,7 @@ def current_csp
175177
def validate_config!
176178
StrictTransportSecurity.validate_config!(@hsts)
177179
ContentSecurityPolicy.validate_config!(@csp)
180+
ReferrerPolicy.validate_config!(@referrer_policy)
178181
XFrameOptions.validate_config!(@x_frame_options)
179182
XContentTypeOptions.validate_config!(@x_content_type_options)
180183
XXssProtection.validate_config!(@x_xss_protection)
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
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: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -312,6 +312,14 @@ module SecureHeaders
312312
end.to raise_error(XPCDPConfigError)
313313
end
314314

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+
315323
it "validates your hpkp config upon configuration" do
316324
expect do
317325
Configuration.default do |config|

spec/spec_helper.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ def expect_default_values(hash)
3030
expect(hash[SecureHeaders::XXssProtection::HEADER_NAME]).to eq(SecureHeaders::XXssProtection::DEFAULT_VALUE)
3131
expect(hash[SecureHeaders::XContentTypeOptions::HEADER_NAME]).to eq(SecureHeaders::XContentTypeOptions::DEFAULT_VALUE)
3232
expect(hash[SecureHeaders::XPermittedCrossDomainPolicies::HEADER_NAME]).to eq(SecureHeaders::XPermittedCrossDomainPolicies::DEFAULT_VALUE)
33+
expect(hash[SecureHeaders::ReferrerPolicy::HEADER_NAME]).to be_nil
3334
end
3435

3536
module SecureHeaders

0 commit comments

Comments
 (0)