Making sure a website is secure should be a priority of every developer. Fortunately, we can often increase security by adding a straightforward HTPP header. In this article, we explain what a man-in-the-middle attack is. We also explain how to deal with some of its examples by adding the HTTP Strict Transport Security (HSTS) header.
Tackling the man-in-the-middle attacks with HTTPS
The man-in-the-middle attack occurs when an attacker intercepts the communication between two parties. The hackers can both eavesdrop on the traffic and modify it. For example, they can set up a Wi-Fi hotspot near a location where people often connect to a public Wi-Fi network. A good example is a hotel or a restaurant. Once the users connect to a malicious Wi-Fi hotspot, the attacker can monitor their online activity and intercept various important information.
When we establish an HTTP connection with a website, we send and receive all data in plain text. The above includes passwords, for example. HTTPS, on the contrary, uses encryption by design. So even if someone intercepts the data, they won’t make much sense out of it.
If you want to know more about HTTPS, check out Node.js TypeScript #8. Implementing HTTPS with our own OpenSSL certificate
A common approach for a server is to accept both HTTP and HTTPS connections. Conveniently, if our users request our website through HTTP, they get redirected to the HTTPS version. While the above behavior is very user-friendly, it might create an opportunity for a man-in-the-middle attack.
Imagine using a Wi-Fi hotspot near a coffee shop and wanting to make a few money transfers in our online banking service. If the network we use is malicious, the attacker can intercept the initial HTTP request. We can deal with this issue with the Strict-Transport-Security response header.
The HTTP Strict Transport Security (HSTS)
With the Strict-Transport-Security response header, the server informs the browser that it should only access the given website using HTTPS.
1 |
Strict-Transport-Security: max-age=31536000 |
The above works only if the user accessed our website using HTTPS at least once and the server responded with the Strict-Transport-Security header. During the time specified in max-age, the browser always redirects to HTTPS automatically. Thanks to that, the browser does not need to rely on the redirect from the server. With max-age=31536000, our browser redirects to HTTPS automatically for a whole year.
Whenever the server sends the Strict-Transport-Security header, the browser updates the expiration time for the given website. The significant thing is that the browser ignores the header if the server sends it through the HTTP connection. Therefore, allowing HSTS through HTTP would enable the attacker to disable it by sending Strict-Transport-Security: max-age=0.
We can also use the includeSubDomains directive to apply the Strict Transport Security rule to all subdomains.
1 |
Strict-Transport-Security: max-age=31536000; includeSubDomains |
Preloading
Besides the above, there is also the preload directive that is not a part of the official specification. Google maintains a list of websites that should never work an insecure connection. Everybody can get their website there using the HSTS preload service. It is used by browsers such as Chrome, Firefox, and Edge.
For google to accept us, we first need to meet a set of requirements. This means that we must:
- have a valid SSL certificate,
- redirect from HTTP to HTTPS on the same host,
- serve all subdomains using HTTPS
- set max-age to at least one year (two years are recommended),
- use the preload directive.
Removing a website from the preload list
If we ever remove the preload directive from our response headers, Google would consider our site as not meeting the requirements. It might be removed automatically, but we can also use the removal form.
Unfortunately, the removal process is not straightforward. Since the HSTS list is hardcoded into browsers, it might take months for the change to reach users with the browser update. The above means that we should be sure that we can support HTTPS for our entire website and its all subdomains in the long term.
Setting the Strict Transport Security header
There are quite a few ways to set up the HTTP Strict Transport Security header. For example, wanago.io uses WordPress. Because that’s the case, we can use the .htaccess file to set up the Strict-Transport-Security response header.
1 2 3 |
<IfModule mod_headers.c> Header set Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" env=HTTPS </IfModule> |
For it to work correctly, we can add the above code at the very bottom of our .htaccess file. Thanks to env=HTTPS, we send the Strict-Transport-Security header only when the user requests the website through HTTPS. Otherwise, the HSTS preload service warns us that it should not happen.
A great tool that we can use to increase the security of our Express-based Node.js applications is the Helmet library. It is a middleware that sets many different HTTP headers to make applications more secure. Besides other headers, it also adds the Strict-Transport-Security.
If you want to know more about Helmet, check out Increasing security of Express applications with the Helmet middleware
When using it, we can set up all of the directives as we wish:
1 2 3 4 5 6 7 |
app.use( helmet.hsts({ maxAge: 63072000, includeSubDomains: true, preload: true, }) ); |
We can also rely on the default values provided by Helmet. It sets maxAge to 180 days, includeSubDomains to true and preload to false.
Summary
This article has gone through the principles behind the HTTP Strict Transport Security rule and its benefits. To do that, we first had to understand the man-in-the-middle attack and what danger it poses. Thanks to HSTS, we can make our applications more sure by enforcing the browsers always to use HTTPS when connecting to our website and not rely on the redirect coming from the server.