diff --git a/lib/rack/ssl.rb b/lib/rack/ssl.rb index 5e4b94e..c991372 100644 --- a/lib/rack/ssl.rb +++ b/lib/rack/ssl.rb @@ -11,6 +11,7 @@ def call(env) if scheme(env) == 'https' status, headers, body = @app.call(env) headers = hsts_headers.merge(headers) + flag_cookies_as_secure!(headers) [status, headers, body] else redirect_to_https(env) @@ -39,5 +40,16 @@ def redirect_to_https(env) def hsts_headers { 'Strict-Transport-Security' => "max-age=16070400; includeSubDomains" } end + + def flag_cookies_as_secure!(headers) + cookies = headers['Set-Cookie'].split("\n") + headers['Set-Cookie'] = cookies.map { |cookie| + if cookie !~ / secure;/ + "#{cookie}; secure" + else + cookie + end + }.join("\n") + end end end diff --git a/test/test_ssl.rb b/test/test_ssl.rb index 84c28b0..c6004cb 100644 --- a/test/test_ssl.rb +++ b/test/test_ssl.rb @@ -7,7 +7,11 @@ class TestSSL < Test::Unit::TestCase include Rack::Test::Methods def default_app - Rack::SSL.new(lambda { |env| [200, {"Content-Type" => "text/html"}, ["OK"]] }) + Rack::SSL.new(lambda { |env| + headers = {'Content-Type' => "text/html"} + headers['Set-Cookie'] = "id=1; path=/\ntoken=abc; path=/; secure; HttpOnly" + [200, headers, ["OK"]] + }) end def app @@ -21,18 +25,26 @@ def test_allows_https_url end def test_allows_https_proxy_header_url - get "http://example.org/path?key=value", {}, 'HTTP_X_FORWARDED_PROTO' => "https" + get "http://example.org/", {}, 'HTTP_X_FORWARDED_PROTO' => "https" assert last_response.ok? end def test_redirects_http_to_https get "http://example.org/path?key=value" assert last_response.redirect? - assert_equal "https://example.org/path?key=value", last_response.headers['Location'] + assert_equal "https://example.org/path?key=value", + last_response.headers['Location'] end def test_strict_transport_security_header - get "https://example.org/path?key=value" - assert_equal "max-age=16070400; includeSubDomains", last_response.headers['Strict-Transport-Security'] + get "https://example.org/" + assert_equal "max-age=16070400; includeSubDomains", + last_response.headers['Strict-Transport-Security'] + end + + def test_flag_cookies_as_secure + get "https://example.org/" + assert_equal ["id=1; path=/; secure", "token=abc; path=/; secure; HttpOnly"], + last_response.headers['Set-Cookie'].split("\n") end end