HSTS: Protecting your website against Man in the Middle attacks

HSTS: Protecting your website against Man in the Middle attacks

Although HTTPS provides a huge security increase when compared to the HTTP protocol, there is still room for improvement. First of all, we can use OCSP stapling to validate the SSL certificate on the server instead of the client, improving both security and speed.

Another known HTTPS issue is that it may be vulnerable to man in the middle (MITM) attacks.

What's a Man in the Middle attack?

Imagine that John is having a conversation with Sandra. To communicate, they trust a third party (in this case, a webserver), to properly deliver the messages between them.

Illustration of John sending a message to Sandra over the cloud

Now, if this conversation is not performed using a secure channel, somebody (Man in the Middle) can intercept the messages, and secretly act as the server, being able to even modify the messages.

Man in the Middle Illustration

How to protect your website against Man in the Middle using HSTS

When you write, for example, learn.techseo.blog in your address bar, your web browser will try to load the HTTP page by default. This happens because the browser cannot assume that your website will have an active TLS certificate, so it will try to load the HTTP version and, if configured, redirect to the HTTPS version.

Image of a redirection from HTTP to HTTPS version

This first connection is unencrypted and, thus, is susceptible to MITM attacks.

HSTS (HTTP Strict Transport Security) tells browsers to only accept secure connections. This is done by using the Strict-Transport-Security header, which specifies the time period (in seconds) in which browsers must only accept connections over HTTPS.

This would be an example of an HSTS configuration of one year:

Strict-Transport-Security: max-age=31536000

For development and staging environments, it's common to use 6 months as the max-age meanwhile, for production, the most common configuration is 2 years.

Maximizing HSTS security

HSTS can be configured to be applied in every subdomain:

Strict-Transport-Security: max-age=31536000; includeSubDomains; preload

If we don't include preload, the browser will use HSTS for every subsequent connection, but not for the first connection.

Submitting our website to the HSTS listing

To make it even better and faster, web browsers periodically download a list of every HSTS preload domain and subdomain. To submit your website to this list you have to fulfill a few requirements:

  1. Serve a valid SSL/TLS certificate
  2. Redirect all traffic from HTTP to HTTPS
  3. Serve all subdomains over HTTPS
  4. Serve an HSTS header with a max-age of, at least, 1 year (it's recommended to use 2 years). This header must contain the includeSubdomains and preload directives.
  5. The HTTP to HTTPS redirection must also contain the HSTS header.

You can submit your website by clicking here. To do it, you just have to introduce your domain name in the top-input and accept the conditions 🥳.

⚠️ Special considerations

Please, be aware that enabling HSTS in a misconfigured domain can cause serious problems, as it would not allow users to access the domain anymore. Please, make sure your SSL certificate, redirections, and headers are properly set before continuing.

Configuring HSTS using .htaccess

We just have to include the following header inside our .htaccess:

Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"

Configuring HSTS using NGINX

NGINX also makes it super easy to configure HSTS:

add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;

Configuring HSTS using PHP

Although it's highly recommended to configure it server-side, there will be times in which we do not have control over our server and we have to configure it by code.

This is how we would do it in PHP:

<?php
header("Strict-Transport-Security: max-age=31536000; includeSubDomains; preload");

Configuring HSTS using Laravel

In Laravel, we can use a global middleware to ensure HSTS:

<?php

namespace App\Http\Middleware;

use Closure;

class EnsureHSTS
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
        return $next($request)
            ->header('Strict-Transport-Security', 'max-age=31536000; includeSubDomains; preload');
    }
}

Configuring HSTS using ExpressJS in NodeJS

Inside their documentation, ExpressJS recommends using Helmet to secure your site using HSTS:

First of all, we need to install Helmet:

$ npm install helmet

Then, we add helmet to our express application:

const express = require("express")
const helmet = require("helmet")

const app = express()

app.use(helmet())

Configuring HSTS using Django

As always, Python provides a super-easy solution to enable HSTS 🥳. To enable it, just add the following configuration on your settings.py:

SECURE_HSTS_SECONDS = 31536000
SECURE_HSTS_INCLUDE_SUBDOMAINS = True
SECURE_HSTS_PRELOAD = True

Recap

We've seen why using HSTS is essential to avoid MITM attacks, as well as how to configure it and submit it to the Preload list. 🤩