Skip to content

Commit

Permalink
Merge pull request #231 from jmera/secure-cookies
Browse files Browse the repository at this point in the history
Secure Cookies
  • Loading branch information
oreoshake committed Mar 16, 2016
2 parents c4c69e1 + 3301a0d commit 76fb828
Show file tree
Hide file tree
Showing 6 changed files with 58 additions and 17 deletions.
20 changes: 10 additions & 10 deletions lib/secure_headers.rb
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,16 @@ def content_security_policy_style_nonce(request)
content_security_policy_nonce(request, CSP::STYLE_SRC)
end

# Public: Retreives the config for a given header type:
#
# Checks to see if there is an override for this request, then
# Checks to see if a named override is used for this request, then
# Falls back to the global config
def config_for(request)
request.env[SECURE_HEADERS_CONFIG] ||
Configuration.get(Configuration::DEFAULT_CONFIG)
end

private

# Private: gets or creates a nonce for CSP.
Expand Down Expand Up @@ -217,16 +227,6 @@ def use_cached_headers(default_headers, request)
end
end

# Private: Retreives the config for a given header type:
#
# Checks to see if there is an override for this request, then
# Checks to see if a named override is used for this request, then
# Falls back to the global config
def config_for(request)
request.env[SECURE_HEADERS_CONFIG] ||
Configuration.get(Configuration::DEFAULT_CONFIG)
end

# Private: chooses the applicable CSP header for the provided user agent.
#
# headers - a hash of header_config_key => [header_name, header_value]
Expand Down
2 changes: 1 addition & 1 deletion lib/secure_headers/configuration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ def add_noop_configuration

attr_accessor :hsts, :x_frame_options, :x_content_type_options,
:x_xss_protection, :csp, :x_download_options, :x_permitted_cross_domain_policies,
:hpkp
:hpkp, :secure_cookies
attr_reader :cached_headers

def initialize(&block)
Expand Down
23 changes: 23 additions & 0 deletions lib/secure_headers/middleware.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
module SecureHeaders
class Middleware
SECURE_COOKIE_REGEXP = /;\s*secure\s*(;|$)/i.freeze

def initialize(app)
@app = app
end
Expand All @@ -8,8 +10,29 @@ def initialize(app)
def call(env)
req = Rack::Request.new(env)
status, headers, response = @app.call(env)

config = SecureHeaders.config_for(req)
flag_cookies_as_secure!(headers) if config.secure_cookies
headers.merge!(SecureHeaders.header_hash_for(req))
[status, headers, response]
end

private

# inspired by https://github.com/tobmatth/rack-ssl-enforcer/blob/6c014/lib/rack/ssl-enforcer.rb#L183-L194
def flag_cookies_as_secure!(headers)
if cookies = headers['Set-Cookie']
# Support Rails 2.3 / Rack 1.1 arrays as headers
cookies = cookies.split("\n") unless cookies.is_a?(Array)

headers['Set-Cookie'] = cookies.map do |cookie|
if cookie !~ SECURE_COOKIE_REGEXP
"#{cookie}; secure"
else
cookie
end
end.join("\n")
end
end
end
end
2 changes: 1 addition & 1 deletion lib/secure_headers/railtie.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ class Railtie < Rails::Railtie
'Public-Key-Pins', 'Public-Key-Pins-Report-Only']

initializer "secure_headers.middleware" do
Rails.application.config.middleware.use SecureHeaders::Middleware
Rails.application.config.middleware.insert_before 0, SecureHeaders::Middleware
end

initializer "secure_headers.action_controller" do
Expand Down
2 changes: 1 addition & 1 deletion spec/lib/secure_headers/configuration_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ module SecureHeaders
config = Configuration.get(:test_override)
noop = Configuration.get(Configuration::NOOP_CONFIGURATION)
[:hsts, :x_frame_options, :x_content_type_options, :x_xss_protection,
:x_download_options, :x_permitted_cross_domain_policies, :hpkp, :csp].each do |key|
:x_download_options, :x_permitted_cross_domain_policies, :hpkp, :csp, :secure_cookies].each do |key|

expect(config.send(key)).to eq(noop.send(key)), "Value not copied: #{key}."
end
Expand Down
26 changes: 22 additions & 4 deletions spec/lib/secure_headers/middleware_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@

module SecureHeaders
describe Middleware do
let(:app) { ->(env) { [200, env, "app"] } }
let(:app) { lambda { |env| [200, env, "app"] } }
let(:cookie_app) { lambda { |env| [200, env.merge("Set-Cookie" => "foo=bar"), "app"] } }

let :middleware do
Middleware.new(app)
end
let(:middleware) { Middleware.new(app) }
let(:cookie_middleware) { Middleware.new(cookie_app) }

before(:each) do
reset_config
Expand Down Expand Up @@ -36,5 +36,23 @@ module SecureHeaders
_, env = middleware.call request.env
expect(env[CSP::HEADER_NAME]).to match("example.org")
end

context "cookies should be flagged" do
it "flags cookies as secure" do
Configuration.default { |config| config.secure_cookies = true }
request = Rack::MockRequest.new(cookie_middleware)
response = request.get '/'
expect(response.headers['Set-Cookie']).to match(Middleware::SECURE_COOKIE_REGEXP)
end
end

context "cookies should not be flagged" do
it "does not flags cookies as secure" do
Configuration.default { |config| config.secure_cookies = false }
request = Rack::MockRequest.new(cookie_middleware)
response = request.get '/'
expect(response.headers['Set-Cookie']).not_to match(Middleware::SECURE_COOKIE_REGEXP)
end
end
end
end

0 comments on commit 76fb828

Please sign in to comment.