What every Web Developer should know about HTTPS Part 3 - Securing your Website

There’s more to deploying HTTPS than just obtaining a certificate, slapping it on your server and calling it a day. Even for brand new websites, and especially for legacy websites, you need to be absolutely sure that under no circumstances can any content on your website be served over HTTP.

Today’s post takes a look at a few things you can do to secure your HTTPS website even further.

301 redirects for HTTP Traffic

Users will often make requests to your website over HTTP, such as when typing the URL in the browser’s address bar. You need to make sure that these HTTP requests are properly 301 redirected to the equivalent HTTPS version.

Here’s an example of how that works. If you make an insecure request to this website, you’ll get the following response:

Chrome DevTools showing 301 response

The browser then follows the address defined in the Location header so that you reach the eventual endpoint.

Chrome DevTools showing 200 OK response

As you can see, the second request returns a 200 OK response because its now made over a secure scheme.

If you’re routing your traffic through Cloudflare, you can enable 301 redirects on HTTP traffic very easily by toggling a switch, and this works regardless of what tech stack your website runs on. Otherwise, you have to implement this manually on your server.

Always Use HTTPS enabled on Cloudflare

Although 301 redirecting HTTP requests is a good first step to enforcing HTTPS on your domain, a couple of problems remain. Firstly, that initial HTTP request that goes over the wire remains vulnerable to a MITM attack.

It is entirely possible for an attacker to intercept that first request and redirect users to another site (such as a phishing site), or they could perform an SSL strip attack where the connection established by the victim’s browser is downgraded from HTTPS to HTTP.

Also, in terms of performance, there’s a penalty to pay for that extra HTTP request that the browser needs to make which is not so great.

We can fix the above problems with HSTS.

HTTP Strict Transport Security (HSTS)

HSTS is a security mechanism that specifies to the browser that it should never interact with a website over an insecure connection for a defined period of time.

To implement HSTS on your website, you need to configure your server to send the strict-transport-security header over a secure connection. The header looks like this:

strict-transport-security: max-age=31536000

This header specifies to the browser that it may not make an insecure request to the site for the next 31,536,000 seconds (365 days). If the browser, for any reason, attempts an insecure request to the website within this time period, a 307 internal redirect is triggered as the browser first upgrades the request to use the secure HTTPS protocol before sending it over the network.

This rule applies to everything on that domain (images, scripts e.t.c). So you have to make sure that you are serving everything over HTTPS otherwise some content will not be loaded.

This is how this process looks like in Chrome:

Chrome DevTools showing 307 Internal Redirect
camera (Large preview)

The scope of HSTS can be extended to all subdomains by using the includeSubdomains directive. Before you do this, be sure that you want to serve all subdomains with HTTPS otherwise your users will blocked from accessing them.

strict-transport-security: max-age=31536000; includeSubdomains

While this solves the problem where the user makes an insecure request to a website that has already been loaded over HTTPS with the strict-transport-security header included in the response headers, one problem remains.

The very first request a user makes to a site, before HSTS kicks in at all, is made over plain HTTP and can be read by anyone with access to the traffic. So if that first request is compromised and the attacker strips out the HSTS header, the user never benefits from this enhancement.

This procedure for implementing HSTS relies on the very first request to that site remaining authentic before it can receive the HSTS header. This is known as Trust On First Use (TOFU).

A potential solution to the TOFU problem is to preload HSTS on the domain.

Preloading HSTS

To preload HSTS on your domain, you need to include the preload directive in the HSTS header like this:

strict-transport-security: max-age=31536000; includeSubdomains; preload
Firefox Dev tools showing the HSTS header on freshman.tech
camera The HSTS header on freshman.tech (Large preview)

This allows you to submit your site for inclusion in the HSTS preload list which all major browser manufacturers (Chrome, Firefox, Opera, Safari, IE 11 and Edge) hardcode into their browsers as being HTTPS only. Once your domain is on this list, and is shipped in these browsers, only secure requests will be made to your domain from the get go.

Graph showing browser support for HSTS
camera Browser support for HSTS is pretty good (Large preview)

Add HSTS to your site with Cloudflare

If you use Cloudflare’s infrastructure, deploying HSTS on your site is a piece of cake. Once you login to your account, click Crypto at the top of the page, then scroll down and find the HSTS module:

HSTS settings on Cloudflare

Click Change HSTS Settings, enable all the relevant options and hit Save.

HSTS options on Cloudflare

You need to be absolutely sure that all your site’s traffic will be served via HTTPS before enabling HSTS otherwise, parts of your site may become inaccessible.

Fixing Mixed Content Warnings and Errors

For the green padlock to be displayed in a browser’s address bar, all the content on a webpage must be loaded over HTTPS. If anything on a page is loaded over HTTP, the green padlock does not appear and you get a Mixed Content warning or error in the browser console.

Mixed Content Warning Example in Firefox
camera HTTPS, but no green padlock

Explicitly specifying the scheme to request embedded content such as images or iframes is a common cause for this problem. Other times, it could be third party code (such as Ads) or user submitted content that cause these warnings to be displayed.

To fix this problem, there are two main approaches you can take and you can definitely combine both solutions to make sure that the majority of your users are covered.

Fix HTTP references

By default, the browser will load passive content (such as images) over an insecure connection even if the parent page is served with HTTPS. It will only remove the green padlock from the address bar.

Firefox DevTools showing mixed content warnings

To correct this, you need to dig into your site’s source code and fix all HTTP references listed in these errors and warnings by changing them to use HTTPS instead.

Before you do that, confirm that those resources can be loaded over HTTPS first. If the resource is unable to load securely, you may need to find a different host for that resource (one that supports HTTPS) or remove the resource from your site.

If your site has many such warnings or errors, finding and fixing each one of them could become very tedious. A search and replace tool could speed things up, or you can just use Cloudflare’s Automatic HTTPS Rewrites feature.

Screenshot of Automatic Rewrites feature enabled on Cloudflare

While this solution works for resources that you explicitly included on your website, it cannot stop third parties from embedding content insecurely. We can prevent that by using a Content Security Policy (CSP).

Content Security Policy (CSP)

Having a Content Security Policy on your site is essential, and can help solve many of the common attacks often performed on websites.

I’m going to cover what CSPs are, how to define one and the types of attacks they mitigate against in later article. For now, let’s go over the CSP rules that are relevant in the context of fixing mixed content warnings and errors.

Upgrade-Insecure-Requests

You can include the following CSP meta tag on your website to upgrade insecure requests to secure ones.

<meta http-equiv="Content-Security-Policy" content="upgrade-insecure-requests">

Or you can send a Content-Security-Policy response header with this directive:

Content-Security-Policy: upgrade-insecure-requests

By using the above CSP, all insecure embeds on your website will be automatically upgraded to the secure version including downstream services that you have no control over, or user submitted content (such as embedded images in comments). And if any content cannot be loaded over HTTPS, it will be not be loaded at all thus preserving the security of your page.

The only downside to this method is that support for upgrade-insecure-requests is non-existent in Microsoft Edge and Internet Explorer. So its best to combine this method with the previous one so as to cover the majority of your users.

Graph showing browser support for upgrade-insecure-requests directive
camera (Large preview)

Block-All-Mixed-Content

Another option is to block all insecure requests with the block-all-mixed-content CSP meta tag, which blocks both active and passive content from being loaded insecurely.

<meta http-equiv="Content-Security-Policy" content="block-all-mixed-content">

or include the header:

Content-Security-Policy: block-all-mixed-content

You need to be pretty sure that you’ve got everything right on your website before including this CSP directive, otherwise it might break features and content that your users expect to be present.

Securing Cookies

If authentication cookies are sent over unencrypted connections, it is possible for an attacker to steal this cookie and hijack the victim’s session. To avoid this, you need to flag your cookies as Secure. This instructs the browser to only load the cookies over an encrypted connection.

You also need to flag cookies as HttpOnly so that they are made inaccessible to JavaScript. Cookies available to JavaScript can get stolen through XSS.

HTTPS Everywhere

A fairly common mistake when implementing HTTPS is to leave the landing page of a website unencrypted because it does not have anything sensitive on it. We have already covered why HTTPS is important for all pages and not just those that collect sensitive information, but I just want to highlight the problem with doing this.

GTBank Homepage

For example, on the above bank website, the homepage is loaded over an insecure HTTP connection. But it contains a link to the login page which is indeed served over HTTPS.

<li>
  <a href="https://ibank.gtbank.com/ibank3/login.aspx" target="_blank">LOGIN</a>
</li>

The problem here is since the homepage is loaded over plain HTTP, you cannot trust anything on it. It is trivial for a MITM to modify the login button on that page to link to a phising page that looks exactly like the original and use that harvest users’ login details.

<li>
  <a href="https://ibank.mygtbank.com/ibank3/login.aspx" target="_blank">LOGIN</a>
</li>

Users would likely be easily fooled since they were redirected to the page from what appeared to be the legit homepage.

This is why its important to have 100% HTTPS coverage across your entire website. It is the only way you can ensure the integrity of the pages on your site.

Summary

So those are a few things you can do to secure your websites and applications even further. All the techniques discussed should be easy for you to implement on your site right away, especially if you use Cloudflare to route your traffic.

The next article will wrap up the series by discussing what HTTPS cannot protect against.