I have set up HTTPS with HSTS pinning for my blog a long time ago, in this post I will aim to explain why those are useful and why I decided to do it on my own personal blog. I will define these terms later but I will assume that you already know what HTTP is and have some idea of what HTTPS is.
HTTPS is the secured version of HTTP. In this context, secured means that the following properties hold (once the connection is established, more on that later):
The good news is that enabling HTTPS for a website nowadays can be done at almost no cost and with minimal configuration. Assuming your website is already up and running and serving HTTP requests, here are steps you can follow to get to HTTPS:
1. If not already done, put your application behind a reverse proxy/webserver like Apache or Nginx
2. Get an SSL certificate from Let's Encrypt, they have well written guides that explain how to set up your server
3. Have your reverse-proxy/webserver do SSL for you (as I said in the previous item, Let's Encrypt has some documentation and you can find easy guides on the internet).
4. There are some steps to take to harden your HTTPS configuration, you can easily find how (even some already made configurations) on the internet.
5. Test it! I recommend you to test your website's configuration using Qualys SSL Server Test, they will tell you if something is amiss!
Once an HTTPS connection is set up, the security properties mentioned above are guaranteed. However, there is one main obstacle in the way of setting up this connection: your clients might use your website using HTTP. The rest of the article focuses on how to make sure they don't.
Let's define a scenario that will help us understand the problem and then show how to make the situation better. Let's say you are a bank called 'Bank Of Trust' and this bank has website called 'bankoftrust.com'. What most people (myself included) will do to access this website is simply type 'bankoftrust.com' in their browser. The problem is that no protocol is specified, the browser will simply default to the insecure HTTP protocol an therefore send your clients to 'http://bankoftrust.com'. An attacker can then easily:
There are two easy solutions to this problem. The first would be to not accept any non-secure HTTP connection, the second is to redirect your clients to HTTPS so that when I type 'bankoftrust.com' and my browser sends me to 'http://bankoftrust.com' then your server redirects me to 'https://bankoftrust.com'.
The second solution is nearly always preferred as most people don't bother specifying the protocol and would be met by an error message (usually a '404 not found' or a simple 'connection refused') when doing so if one does not redirect.
There is one small issue with the redirection solution. Since the initial request is not secured, an attacker could hijack this first request and send back a redirection to another secured domain that they control such as 'https://bankоftrust.com'. HTTPS would prevent them from acting as your bank but this second URL actually contains a cyrillic 'о' instead of the normal english 'o'. It is a different character, they can therefore register this domain and get a certificate for it. If they manage to redirect you to this domain, they can then serve a fraudulent copy of the bank's website and steal your secrets.
The solution here might be to have browsers default to HTTPS. As of writing, this is not the case in the default configuration of browsers. Fortunately, you can instruct browsers to remember that your domain is HTTPS-only, this is what the HSTS header is for! HSTS stands for HTTP Strict Transport Security. You can have your server set this header when it serves HTTPS requests, here is how it looks for my blog:
Strict-Transport-Security: max-age=31536000; includeSubdomains; preload
max-ageset to 31536000 means that the Strict Transport policy is valid for 31536000 seconds (a year). In other words, once a response with this header has been received by the browser, the browser will use only HTTPS with this website for a year.
includeSubdomainsmeans that the policy is valid for subdomains (for instance 'ebanking.bankoftrust.com')
preloadwill be explained in greater details in the next section
With this header set, we guarantee that after the first visit on a website, no insecure connection will be made for at least a year by the same person. The question of the initial insecure request remains, and will be solved in the next section.
Important note: if you set the
max-age to a year, you lock yourself in HTTPS for a full year, make sure your HTTPS setup (certificates, configuration etc...) is stable before moving there. You can start with a smaller
max-age, an hour for instance, in the beginning to make sure everything is fine.
If we want to REALLY avoid insecure HTTP at any cost, there is a way to ask the browser to NEVER use it. This way is called HSTS pinning, and basically consists of a list of domains, hard-coded in browsers source code, that only want to serve HTTPS queries. Browsers who encounter a domain in this list will simply refuse to do any insecure HTTP to access them. To get your domain pinned, you need to add the
preload keyword to the HSTS header (as shown above). You should then submit it to be added to the list on this website: https://hstspreload.org. Instructions are pretty straightforward, you can see that my blog is properly preloaded: https://hstspreload.org/?domain=jrabasco.me. This list is shared by most modern browsers.
If you want more information on the technicality of HSTS and HSTS pinning I suggest this very well written blog post by Troy Hunt: https://www.troyhunt.com/understanding-http-strict-transport/.
While HSTS pinning does 99.999999% of the job, there is still a possibility that some clients get a version of the browser that does not have the pinning list/mechanism in place. This might happen for different reasons, including but not limited to:
All of these mainly mean that, ultimately, it is up to the user to protect themselves, HSTS pinning (and any security measure) will not help people who are behaving recklessly.
It is also important to note that this forces you to be on HTTPS forever, and ever. It is really hard to get removed from the list as update to browsers take time and some users might not upgrade theirs for months. If, for some reason, you think that may be an issue (some legacy piece of code for instance), fix these issues first.
This Blog is not a banking website. I do not exchange secrets with my users. The only privileged part of it is the administration interface, and frankly I don't think there is much to steal there. My main risk would be someone hijacking connections to my blog to pretend I posted something I did not post, I am not a really public personality so I am not sure anybody would benefit from doing this. So why did I decide to setup all of these security layers on this website?
The first reason is fun obviously, I am a software engineer and I have fun building things, and understanding how you make a website securely is fun to me. This is also the reason this website's SSL configuration gets a whopping A+ on the Qualys SSL Server Test (at the time of writing at least!).
The second reason is the more meaningful one though. Part of the reason modern web browsers still default to HTTP is because there are way too many websites who still implement HTTPS improperly or incompletely, i.e. at least some part of their assets or API endpoints would not work over HTTPS. If I want to advocate for a web where everybody uses HTTPS, I feel like I have to start by my own website. On top of that, it is extremely easy to setup nowadays and costs nothing. I really wanted to do the right thing and show a good example for anybody stumbling upon this website. It is our responsibility, as (professional) developers to spread the word and make sure as many people as possible know about these sort of things. The hope here is that one day browsers will make it very hard to navigate with insecure HTTP and make the web a safer place. But for that to happen we all need to take our responsibility and use HTTPS!