Secure HTTPS setup with Cloudflare - PART 2
Learn how to secure a Ghost blog installation on Digitalocean with Cloudflare and SSL hardening.
In Part 1 I explained how to get up and running with a Ghost blog on Digitalocean. In this article I will explain how to enhance the set up by using Cloudflare and by hardening the SSL configuration of the blog.
( Photo by Chris Barbalis )
This article is part of a series
- Part 1 - Setting up a self-hosted Ghost blog on Digitalocean
- Part 2 - Secure HTTPS setup with Cloudflare
- Part 3 - Infrastructure as Code with Terraform and Cloudflare
When it comes to security I tend to be on the paranoid side of paranoia. Normally I'm the guy who checks the door multiple times to make sure it's really locked, arrives 3 hours earlier than scheduled at the airport, and so on. With that said, I still think that in 2019 anything available over HTTP on public internet should use the secure version of that protocol. There is absolutely no reason not to do it.
Consider this: when you open a web page that isn't encrypted, you really have no way of telling whether someone is messing with the content being served to you browser (a typical "man in the middle" scenario). This can become particularly nasty if the 'content' being hijacked is not what you see, but rather what is being executed in your browser. This is being brought to general public attention multiple times, for example in the recent Magecart hacks affecting British Airways and other leading brands.
To be clear, setting up certificates and enabling strong encryption is not all that is needed to secure a web page, but it is certainly the required starting point.
Even a simple site like this blog could be a threat, despite the fact that, if I've set it up right 😅, I'm the only one adding content, no payments are taken, no ads are served (to avoid malvertising issues) and no complex logic or functions are available to external users. Someone could still hijack DNS or inject malicious code that is then delivered and executed by any browser rendering the content.
Something like this would be much harder to achieve if the connection between your browser and the server is secured, and the domain configuration is safe.
So, how far I've gone with the secure set up? Let's have a look
Looking good isn't it? There is quite a lot going on in the two above screenshots so let's focus on the top one first and see what could be the steps to get there.
Step 1 - Install Ghost with SSL certificate
When I installed Ghost in Part 1 (after pointing my domain to the Droplet) I provided all the required information to the ghost installer to initiate the automatic provisioning of a certificate through Let's Encrypt. The installer, once you provided the required information, will take care to auto-provision a valid certificate for your chosen domain using acme.sh, then takes care of adding the relevant configuration lines in nginx to ensure it is installed and used when the pages are served to users.
The beauty of Let's Encrypt - which is supported transparently by Ghost - is that the entire process (including certificate renewal) is automated and it's so much easier if compared to the past. Before, Â you would have endured a lengthy manual set up with a lot of administrative steps and a hefty sum to pay for the certificate itself.
At this point you already have a certificate installed, with a decent configuration applied by default. Technically I could have happily stopped there ... but why, why not just tumble down the rabbit hole instead?
Step 2 - Set up Cloudflare to sit in front of your Droplet
Cloudflare is a worldwide cloud network provider. In a nutshell, they secure and accelerate any website. You can register for free and gain access to a host of awesome free features, alongside other paid features that you might seriously want to consider depending on your use case.
If your domain is registered with them, then some steps are going to be even more straightforward, if not you will just have to update the DNS NS (Nameserver) records for your domain to point to Cloudflare's nameservers. This in turn will allow them to do their magic, such as:
- Caching and serving content faster: because they have a global network of nodes they effectively act as a CDN. A request to your website will be resolved by the Cloudflare Nameservers to direct at the closest Cloudflare datacenter for the originator. Cloudflare will then contact your server to retrieve the content, which is then cached and can be used for other subsequent request. This means that, for free, both the load and the outbound bandwidth on your server will be reduced.
- Threat detection: Your site will be accessed by regular users, as well as by bots and other malicious actors. Cloudflare are able to identify these threats and block their request before they even reach your Server, by either banning them outright or by presenting a challenge for less clear cut cases. This prevents potential issues and removes bogus traffic, in turn reducing again the load hitting your server.
- State of the art HTTPS set up: your visitors will be connecting to Cloudflare first, and not directly to your server. This means that Cloudflare is responsible for the SSL set up and you will be able to easily configure most of the properties in their console.
For the last point, let's see what I have configured:
- DNSSEC: as they are my registrars, this is already enabled by default. DNSSEC is an important security mechanism which prevents people from exploiting the DNS lookup mechanism and 'claim' that they are in charge of it. DNSSEC defeats this by the means of digital signatures: these are verified by clients to ensure that the DNS responses obtained are authentic, and not forged. This page gives a nice, more thorough explanation.
- Full (Strict) SSL: with this setting, Cloudflare will be able to set up a secured, authenticated connection between their datacenter and your origin server. Because we did set up a valid Let's Encrypt certificate (which is a fully-fledged certificate with a valid trust chain) this is the best option available, as it means that the entire channel is secured.
- Edge Certificates: in this case I have used Cloudflare Universal SSL certificate using SNI. You have the (paid) option to obtain a dedicated certificate for your domain only.
- Always Use HTTPS : On
- HSTS: On , with a max age of 12 months. Learn more about HSTS on the OWASP site.
- Authenticated Origin Pulls: On - but this requires some further configuration on your droplet (*)
- Minimum TLS version: I set this to 1.2 . This means that older browsers / clients unable to support at least this standard will not be able to connect to my blog. Tough luck! 😂 Of course, in scenarios where a compromise between security and supporting old clients is required, you would have to lower the bar.
- TLS 1.3: you may want to enable this or not, this is still experimental. I decided to give it a try.
If you want to know more about TLS (1.2 and 1.3) you can read my articles below:
You can find other important optimisations in the 'Speed' and 'Caching' tabs, most of these are self explanatory.
Step 3 - Configure Authenticated Origin Pulls + SSL Hardening
Authenticated Origin Pulls let origin web servers strongly validate that a web request is coming from Cloudflare. We use TLS client certificate authentication, a feature supported by most web servers, and present a Cloudflare certificate when establishing a connection between Cloudflare and the origin server. By validating this certificate in origin server configuration, access can be limited to Cloudflare connections. (source)
(*) In order to let your nginx webserver authenticate the requests you will have to set up the following in your nginx configuration files for your domain ( /etc/nginx/sites-enabed/yourdomain.conf ). Make sure you have downloaded and installed the Cloudflare certificate in the location referred to in the below block (or use your own location)
server {
listen 443 ssl http2;
listen [::]:443 ssl http2
[...]
ssl_client_certificate /etc/ssl/certs/cloudflare.crt;
ssl_verify_client on;
[...]
}
Other nginx SSL configurations
I have also reviewed the options available in nginx and made a few tweaks.
server {
[...]
gzip off;
location / {
[...]
proxy_hide_header X-Powered-By;
}
}
Disabling gzip compression to mitigate against attacks such as BREACH.
The last line is just removing the X-Powered-By header in all HTTP responses, we don't want to leak unnecessary information to the external world.
I have then tweaked the /etc/nginx/snippets/ssl-params.conf (which was included in my above configuration file and contains general SSL configuration parameters). To set up what is best for you, I strongly recommend to read the following guidelines from Mozilla and use the generator to find the perfect combination. Do remember that with the Cloudflare set up with authenticated origin pulls, only Cloudflare will be accessing your webserver - so the settings that ultimately matter for your visitors are the ones you already have set up in Cloudflare.
To provide another level of protection, I have also set up a Cloud Firewall on Digitalocean which is only allowing connections on port 443 from a whitelist of Cloudflare IPs.
Last but not least, you will want to perform an HTTP 301 redirect from http to https. Not really going to be used since we locked down access via Cloudflare and are already enforcing HTTPS at that layer, consider it a best practice.
server {
listen 80;
listen [::]:80;
server_name yoursite.com www.yoursite.com;
return 301 https://www.yoursite.com$request_uri;
}
That's pretty much it. Is it though?
In fact, let's not forget that despite all the tinkering with the nginx settings, selecting super secure ciphers, leveraging Cloudflare etc... it is all pretty much pointless if you don't properly secure the administrative access to your system and configuration dashboards. It may sound daft, but then you read how multiple US government domains have been hijacked and their traffic directed to malicious servers. How did that happen?
Using the following techniques, attackers have redirected and intercepted web and mail traffic, and could do so for other networked services:
- The attacker begins by compromising user credentials, or obtaining them through alternate means, of an account that can make changes to DNS records.
[...]
So, let's go ahead and secure login access to your registrar. Enabling multi-factor access in addition to a strong password will definitely help avoiding the above problems, so hopefully they will support it. If you use Cloudflare it's pretty easy, just follow the steps here. Going back to Digitalocean, they also supports it for the web administrative access, and we already seen how to set it up on SSH for your Droplet in Part 1.
Let's call it a day, shall we? Have I gone over the top? Yes. Is this close to paranoia? Probably. Was it fun? Absolutely! Time to bask in the glory with a good song...