Securing your self-hosted website with Let’s Encrypt, part 4: hardening default setups and avoiding known vulnerabilities

In part 3, we looked at how to finally use Let's Encrypt to issue and renew certificates for our domains. But I also finished with a terrifying cliffhanger: basic HTTPS setups can be vulnerable to attacks! Gasp...!

Let me start by clarifying that I am not a security expert and if someone breaks into your system I will take no responsibility whatsoever, lalalala...

What makes basic HTTPS setups vulnerable?

There are two main culprits:
  • using older protocols and ciphers
  • and using defaults at web app or browser level

Why is using older protocols a bad idea?

SSL, the initial protocol used for encryption, has been proved insecure (see: POODLE). It is recommended that people use the newer TLS protocol instead.

Servers such as nginx will offer SSL by default if the value is not explicitly set in the server configuration file. Also, many configuration samples you find around the internetssss also suggest using it, or the weakest versions of the protocols!

Remember when we talked about negotiating the HTTPS connection? If we offer weaker versions, there's a chance they'll be chosen! Not the best idea...

So: enable TLS, which is safer (and maybe faster), and also perhaps don't offer the insecure protocols at all.

For example, this would be a good change for your nginx configuration:

- ssl_protocols SSLv3 TLSv1 TLSv1.1 TLSv1.2; # nginx default
+ ssl_protocols TLSv1 TLSv1.1 TLSv1.2;

Basically, we removed SSL.

There's a caveat: older clients might not be able to access your site if they do not implement any of the protocols your server offers.

What about using older ciphers?

Likewise, the issue with offering older ciphers is that some of them have been broken, and if you offer encryption with those you might be making it easy for the communications to be deciphered. Not what you want!

But where there are just SSL and TLS and a few version numbers to choose from for protocols, there's an incredibly LONG LIST of ciphers and combinations and sizes you can choose for your server. Should you use ECDHE-ECDSA-AES256-GCM-SHA384 or maybe ECDHE-RSA-AES256-GCM-SHA384? Or perhaps ECDHE-ECDSA-CHACHA20-POLY1305? (Chacha! Chachacha! 🎶)

What difference is there? Which one is better? How are we supposed to know?!


Trust the experts

My answer is to exercise a giant leap of faith and trust the experts.

For example, the security team at Mozilla have put together the SSL Config Generator which lets you interactively (guess what) generate config files for various servers: Apache, Nginx, Lighttpd...

Mozilla SSL Config generator

It will also tell you which are the oldest clients that can connect to your server if you put that configuration in place, depending on the settings you choose. Very handy.

Use strong dhparams

To avoid Logjam attacks, first generate a dhparams.pem file per domain (this might take a bit of time):
openssl dhparam -out /var/www/ 2048

And then use it in your server configuration. For example, with nginx:

ssl_dhparam /var/www/;
ssl_ecdh_curve secp384r1;

Web Application level security

Just as a properly configured server is important for establishing a safe baseline, what happens on the front-end is important too. One of the most useful things you could do is enabling Strict Transport Security. In fact, the Mozilla SSL generator already adds it to the files it generates.

When this policy is enabled, browsers get really strict (ha) about demanding https content. After the first visit to a domain with https, they will not accept http:// links to that domain anymore--they will automatically turn them into https:// instead.

This can help prevent 'man in the middle' attacks.

As I said, I am not an expert on this field and there's a ton more things you can do to make sure your web site is safer. For example, disabling the site from being included in an frame or iframe by using the X-Frame-Options header--so I encourage you to do your research!

This seems like a lot 😧

And it is!

The good thing is that there are services that help you test your server configuration. Two of the most popular are:

Example with SSL Labs SSL test

I quite like this one because you don't have to install anything on your machine--it runs entirely online. You can also ask it to not to publish your results on their homepage.

Before: C-

This was the first site where I used Let's Encrypt and I was convinced I would get an straight A! Grading in SSL test: C-

Not so fast! I was using the default nginx settings so SSL was enabled (instead of TLS). The server was vulnerable to POODLE attacks. Also there was no support for Forward Secrecy either, and the key exchange parameters were weak. How awful of me!

I searched and tried various configurations until the grade went up to A+!

After: A+

Grading SSL test: A+

Oh my god... it's full of resources!

There are way more resources and tutorials that I could link to, but these are a good start to get better at securing servers: I want to insist that there's no way that the systems are always 100% secure. Software is made and configured by humans, and where there is humankind there will be errors and accidents ;-)

Next part: now that we have HTTPS, what else can we do apart from, obviously, serving content via HTTPS?