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.
Flaky tests are tests that sometimes fail for no obvious reason. They are the plague of many end-to-end (E2E) test suites that automate the browser through tools like Capybara and Selenium.
Join our free training event and learn to fix any flaky test suite, even in large legacy applications.