Posted almost 4 years ago. Visible to the public.

Rails: Flagging all cookies as secure-only to pass a security audit

Why secure-only cookies used to be necessary

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.

Why secure-only cookies are no longer necessary

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.

Why you might need secure-only cookies anyway

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.

Rails: Automatically flag all cookies as secure-only

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:

Copy
# 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:

Copy
require 'middleware/secure_cookies' config.middleware.insert_after ActionDispatch::Static, Middleware::SecureCookies

Add a test to spec/requests/secure_cookies_spec.rb:

Copy
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.

By refactoring problematic code and creating automated tests, makandra can vastly improve the maintainability of your Rails application.

Owner of this card:

Avatar
Henning Koch
Last edit:
almost 4 years ago
by Arne Hartherz
About this deck:
We are makandra and do test-driven, agile Ruby on Rails software development.
License for source code
Posted by Henning Koch to makandra dev
This website uses short-lived cookies to improve usability.
Accept or learn more