Security Tip: How Strict Is your Transport Security?

[Tip #82] HTTPS is everywhere & easy, but HTTP is still an option... How do you stop an attacker intercepting and downgrading connections to your site?

Security Tip: How Strict Is your Transport Security?

Last week we looked at the Referer security header, so we've moving onto another header this week: Strict-Transport-Security.

The HTTP Strict-Transport-Security (HSTS) header informs the user's browser that the current site should only be accessed via HTTPS - preventing it from loading the site via HTTP entirely.

This is an important header to pay attention to, as it's designed to prevent Downgrade attacks via a Person in the Middle (PitM/MitM) attack on the connection.

Downgrade attacks work by intercepting encrypted connections and forcing the connection to a weak or unencrypted version, which allows the attacker to read and modify the messages/requests.

Normally the connection from the Browser looks like this:

 ---------        HTTPS        --------
| Browser |     <------->     | Server |
 ---------                     --------

Normal HTTPS Request

However, if the Attacker can intercept the request, they can tell the Browser to use HTTP only, while maintaining the HTTPS connection with the Server themselves:

 ---------        HTTP        ----------        HTTPS        --------
| Browser |     <------>     | Attacker |     <------->     | Server |
 ---------                    ----------                     --------

PitM Downgrade Attack

This allows the attacker to read and modify all requests passing through. This means they have full access to passwords, payment details, private or sensitive information, etc, as well as make any changes or run any commands on the compromised site.

The Strict-Transport-Security header prevents this attack by telling the Browser it requires an encrypted HTTPS connection. If the Attacker (or Server) tries to use an unencrypted HTTP connection, the Browser will reject it and display a warning instead. It will always require a valid encrypted HTTPS request, and block or ignore any redirects or attempts otherwise.

Since an attacker could easily strip out the header, the browser remembers it after the first time it sees it for max-age seconds. This allows the browser to reject any future unencrypted HTTP requests (within the max-age) automatically, keeping the site protected.

For example, this instructs the browser to require HTTPS for 1 year:

Strict-Transport-Security: max-age=31536000

Adding the includeSubDomains directive instructs the browser to apply HTST to the domain and all subdomains for 1 year, allowing the first request to anywhere on the domain to store the HSTS requirement.

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

How Long For max-age?

Most sites should only be accessed via HTTPS, so going for a long expiry of 1-2 years is recommended. This keeps the site protected across infrequent visits.

That said, if you have an older domain, with a bunch of different subdomains, hosting different apps, you may have some areas that need HTTP only. If you're worried about this, you could start with a short max-age to see if anything breaks and slowly work up. I.e. 5 minutes, 1 day, 1 week, 1 month, etc. However, this is becoming much less common as HTTPS is much easier than it used to be, and browsers are actively warning users on HTTP sites.

If you do need HTTP somewhere, I would recommend migrating those sites onto a separate domain - to isolate them from your primary domain and apps.

Preloading, aka the Trust On First Use (TOFU) Problem

You may have spotted one of the downsides of the HSTS header already: the browser gets the HSTS header on the first request.

This means if an attacker is already intercepting traffic, they can strip the header while downgrading to HTTP to prevent the Browser from knowing it should have an encrypted connection.

This is called the Trust On First Use (TOFU) problem.

HSTS gets around this with the preload directive and the Preload List. The Preload List is a list of hardcoded domains within the major browsers (Chrome, Firefox, Safari, etc), which lists all of the domains which require HTTPS. The browser will not make an HTTP request to these domains, preventing an attacker from intercepting any requests or attempting downgrade attacks.

To get your site on the Preload list, you need to enable HSTS with max-age of at least 1 year, include subdomains with includeSubDomains, and add the preload directive.

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

HSTS header for Preload

Once that's live and working, you can submit it to the Preload List at, and it will be included in browsers in a future release.

Note: Removal from the Preload List may take months or even years. So make sure you're aware of the implications before adding preload and submitting your domain.

Some newer domain extensions, notably .dev and .app, are already included in the Preload List, which helps enforce security of any apps that use those extensions. So if you're using one of these, you're already protected. 🙂

When was your last security audit or penetration test? Book in a Laravel Security Audit and Penetration Test today!
Looking to dive deeper into Laravel security? Check out Practical Laravel Security, my hands-on security course that uses interactive hacking challenges to teach you about how vulnerabilities work, so you can avoid them in your own code!