Cookies
Archive
have an optional secure
flag. It tells the browser to not send the cookie for a non-https request.
It used to be important to activate the secure
flag even on sites that automatically redirect users from http://
to https://
. The reason was that most users will only enter a scheme-less domain like makandra.de
into their location bar, which will default to http://makandra.de
in any browser. Even though the site will immediately redirect to https://makanda.de
, cookies from a prior visit will already have appeared on the unencrypted wire during the first request to http://makandra.de
.
Today, all sites should use https://
. They should also set a
HSTS header
Archive
which makes browsers default to https://
instead of http://
when the user enters a scheme-less domain into their browser's URL bar.
For a page that exclusively uses https://
with HSTS, it is not necessary to set the secure flag on your cookies. There is simply no case when the browser would talk to the server via unencrypted http://
requests.
A security audit will still raise missing "secure" flags as an issue that needs to be fixed.
It's usually easier flag all your cookies as secure-only, than it is to explain why your application does not need secure cookies.
In a Ruby on Rails app you can add a middleware that automatically sets the Secure
flag to all server-set cookies. The flag is only added for secure requests, so cookies will still work for local development where you might still use http://
.
If you set any cookies from JavaScript, this isn't fixed by the middleware.
Add this to lib/middleware/secure_cookies.rb
:
# On HTTPS requests, we flag all cookies sent by the application to be "Secure".
#
module Middleware
class SecureCookies
COOKIE_SEPARATOR = "\n".freeze
def initialize(app)
@app = app
end
def call(env)
status, headers, body = @app.call(env)
if headers['Set-Cookie'].present? && Rack::Request.new(env).ssl?
cookies = headers['Set-Cookie'].split(COOKIE_SEPARATOR)
cookies.each do |cookie|
next if cookie.blank?
next if cookie =~ /;\s*secure/i
cookie << '; Secure'
end
headers['Set-Cookie'] = cookies.join(COOKIE_SEPARATOR)
end
[status, headers, body]
end
end
end
Add this to your Middleware stack in the middle of config/application.rb
:
require 'middleware/secure_cookies'
config.middleware.insert_after ActionDispatch::Static, Middleware::SecureCookies
Add a test to spec/requests/secure_cookies_spec.rb
:
describe Middleware::SecureCookies do
it 'flags all cookies sent by the application as secure' do
get 'https://www.example.com/test/set_cookie'
response.headers['Set-Cookie'].should =~ %r(test=\S+; path=/; Secure$)
end
it 'will not flag cookies as secure when HTTPS is not being used (in development and tests)' do
get 'http://www.example.com/test/set_cookie'
response.headers['Set-Cookie'].should include('test=') # Cookie is still set
response.headers['Set-Cookie'].should_not include('; Secure')
end
end
Note that /test/set_cookie
must be an existing route that sets a cookie.