Rails: Flagging all cookies as secure-only to pass a security audit
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
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
Today, all sites should use
https://. They should also set a
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
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
Add this to
# 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
require 'middleware/secure_cookies' config.middleware.insert_after ActionDispatch::Static, Middleware::SecureCookies
Add a test to
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
/test/set_cookie must be an existing route that sets a cookie.