August 24, 2016
This blog is part of our Rails 5 series.
Adding HTTPS support is one of the first steps towards enhancing the security of a web application.
Even when a web app is available over https, some users may end up visiting
the http version of the app, losing the security https provides.
It is important to redirect users to the https URLs whenever possible.
We can force users to use HTTPS by setting config.force_ssl = true.
If we look at Rails source code, we can see that when we set
config.force_ssl = true, a middleware ActionDispatch::SSL, is inserted into
our app’s middleware stack :
if config.force_ssl
middleware.use ::ActionDispatch::SSL,config.ssl_options
end
This middleware, ActionDispatch::SSL is responsible for doing three things :
Redirect all http requests to their https equivalents.
Set secure flag on cookies to tell browsers that these cookies must not be
sent for http requests.
Add HSTS headers to response.
Let us go through each of these.
In Rails 5, we can configure the behavior of redirection using the redirect
key in the config.ssl_options configuration.
In previous versions of Rails, whenever an http request was redirected to
https request, it was done with an HTTP 301 redirect.
Browsers cache 301 redirects. When forcing https redirects, if at any point we
want to test the http version of the page, it would be hard to browse it,
since the browser would redirect to the https version. Although this is the
desired behavior, this is a pain during testing and deploying.
Rails 5 lets us
specify the status code for redirection,
which can be set to 302 or 307 for testing, and later to 301 when we are
ready for deployment to production.
We can specify the options for redirection in Rails 5 as follows :
...
config.force_ssl = true
config.ssl_options = { redirect: { status: 307, port: 81 } }
...
If a redirect status is not specified, requests are redirected with a 301
status code.
There is an upcoming change to make
the status code used for redirecting any non-GET, non-HEAD http requests to
307 by default.
Other options accepted by ssl_options under redirect key are host and
body .
By setting the Secure flag on a cookie, the application can instruct the
browser not to send the cookie in clear text. Browsers which support this flag
will send such cookies only through HTTPS connections.
Setting secure flag on cookies is important to prevent cookie hijacking by man in the middle attacks.
In case of a "man in the middle" attack, the attacker places oneself between the
user and the server. By doing this, attacker aims to collect cookies which are
sent from user to server on every request. However, if we mark the cookies with
sensitive information as Secure, those cookies won't be sent on http
requests. This ensures that the browser never sends cookies to an attacker who
was impersonating the webserver at an http end point.
Upon enabling config.force_ssl = true, the ActionDispatch::SSL middleware
sets the Secure flag on all cookies by default.
HSTS or "HTTP Strict Transport Security" is a security enhancement by which applications can specify themselves as HTTPS-only to complying browsers.
HSTS capabilities of a browser can be used by sending appropriate response
headers from the server. When a domain is added to the HSTS list of a browser,
the browser redirects to the https version of the URL without the help of the
server.
Chrome maintains an HSTS Preload List with a list of domains which are hardcoded into chrome as HTTPS only. This list is also used by Firefox and Safari.
Rails 5 has a configuration flag to set the preload directive in the HSTS
header and can be used as follows :
config.ssl_options = { hsts: { preload: true } }
We can also specify a max-age for the HSTS header.
Rails 5 by default sets the max-age of HSTS header to 180 days, which is
considered as the lower bound by
SSL Lab’s SSL Test . This period is also
above the 18 week requirement for HSTS max-age mandated for inclusion in
browser preload list.
We can specify a custom max-age by :
config.ssl_options = { hsts: { expires: 10.days } }
In Rails 5, if we disable HSTS by setting :
config.ssl_options = { hsts: false }
Rails 5 will set the value of expires header to 0, so that browsers immediately stop treating the domain as HTTPS-only.
With custom redirect status and greater control over the HSTS header, Rails 5
lets us roll out HTTPS in a controlled manner, and makes rolling back of these
changes easier.
If this blog was helpful, check out our full blog archive.