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.