I’ve just completed Ed Harmoush’s Practical TLS course; I got a ton of value from relearning some of the fundamental concepts now that I had a few years of real-world experience troubleshooting TLS related issues.
After completing the course, I went ahead and made sure this blog received an A+ SSL Labs rating by making a few small changes in Cloudflare. In this post, I’m describing how I did it, and I’m adding a few extra steps you could take to future proof your rating.
The short of it: achieving the coveted A+ SSL Labs rating with a free Cloudflare account is easy and only requires two changes:
- Set Minimum TLS Version to TLS 1.2
- Activate HSTS and set max-age to >= 6 months
Change Minimum TLS Version to TLS 1.2
By default, Cloudflare’s minimum TLS version is set to TLS 1.0. I don’t have to serve older IoT/embedded devices that only support insecure TLS/SSL versions–I’m hosting a blog, readers are using modern browsers. Those browsers have supported TLS 1.2 since 2008 and TLS 1.3 since 2018. TLS 1.2 is still considered to be secure, and TLS 1.3 is considered very secure.
Go to SSL/TLS and click on Edge Certificates. Scroll down until you get to Minimum TLS Version–change it to TLS 1.2.
You can verify the removal of TLS 1.0 & TLS 1.1 support with the Nmap script
ssl-enum-ciphers. In addition to the supported TLS versions, the script also includes a list of cipher suites the server supports.
$ nmap --script=ssl-enum-ciphers -p T:443 netletic.com Starting Nmap 7.92 ( https://nmap.org ) at 2021-09-17 13:13 IST ... PORT STATE SERVICE 443/tcp open https | ssl-enum-ciphers: | TLSv1.2: | ciphers: | TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA (ecdh_x25519) - A | TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 (ecdh_x25519) - A | TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 (ecdh_x25519) - A | TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA (ecdh_x25519) - A | TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 (ecdh_x25519) - A | TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 (ecdh_x25519) - A | TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 (ecdh_x25519) - A | TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256-draft (ecdh_x25519) - A | TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA (ecdh_x25519) - A | TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 (ecdh_x25519) - A | TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (ecdh_x25519) - A | TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA (ecdh_x25519) - A | TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 (ecdh_x25519) - A | TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (ecdh_x25519) - A | TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 (ecdh_x25519) - A | TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256-draft (ecdh_x25519) - A | TLS_RSA_WITH_AES_128_CBC_SHA (rsa 2048) - A | TLS_RSA_WITH_AES_128_CBC_SHA256 (rsa 2048) - A | TLS_RSA_WITH_AES_128_GCM_SHA256 (rsa 2048) - A | TLS_RSA_WITH_AES_256_CBC_SHA (rsa 2048) - A | TLS_RSA_WITH_AES_256_CBC_SHA256 (rsa 2048) - A | TLS_RSA_WITH_AES_256_GCM_SHA384 (rsa 2048) - A | compressors: | NULL | cipher preference: client | TLSv1.3: | ciphers: | TLS_AKE_WITH_AES_128_GCM_SHA256 (ecdh_x25519) - A | TLS_AKE_WITH_AES_256_GCM_SHA384 (ecdh_x25519) - A | TLS_AKE_WITH_CHACHA20_POLY1305_SHA256 (ecdh_x25519) - A | cipher preference: client |_ least strength: A
If you don’t have access to Nmap, you could also use the
openssl utility and specify a specific TLS version. If the reply answers with the leaf and the certificate chain, the TLS version you tried is supported.
# or -tls1_1, -tls_1_2, -tls1_3... $ openssl s_client -connect netletic.com:443 -tls1_0
Weak Cipher Suites
SSL Labs gives some of the TLS 1.2 cipher suites a WEAK rating. You can’t disable those cipher suites with a Cloudflare free account, but it’s good to know why SSL Labs rates them that way.
SSL Labs assigns this WEAK rating for two reasons:
- No AEAD, as is the case for (1), (2), (3), and (4)
- No Forward Secrecy, as is the case for (4) and (5)
AEAD & Forward Secrecy are mandatory for Cipher Suites in TLS 1.3 but optional for TLS 1.2.
AEAD (Authenticated Encryption with Associated Data)
AEAD is a combines a cipher and a MAC (Message Authentication Code) and offers authenticated encryption following the MAC-and-Encrypt method instead of MAC-then-Encrypt.
AES-CBC with HMAC as implemented in TLS uses the MAC-then-Encrypt method and has accumulated many vulnerabilities.
At its core, this is because the receiver has to reverse the MAC-then-Encrypt method to verify whether the message was tampered with. I.e., the receiver has to decrypt the message before it can verify its authenticity. In addition, AES-CBC uses fixed-size blocks and adds padding to the last block of plaintext. But the padding is not included in the MAC, which opens the cipher up to padding oracle attacks.
Conversely, cipher suites with AES-GCM and ChaCha20-Poly1305 get a STRONG rating because they follow the AEAD cipher mode.
AES-GCM turns the block cipher AES into a stream cipher. It combines every plaintext block with a Nonce and an incrementing counter. It also adds authentication using a GMAC. ChaCha20-Poly1305 supports the AEAD cipher mode, where ChaCha20 is the stream cipher, and Poly1305 is the MAC scheme.
AES-GCM ciphers rate WEAK because they don’t do AEAD.
AES-GCM and ChaCha20-Poly1305 do AEAD and are rated STRONG.
Forward secrecy means once encrypted, always encrypted. With the RSA Key Exchange, any encrypted application data intercepted and stored in the past can be decrypted in the future if the server’s private key ever leaks and if the attacker also captured the initial TLS handshake. The attacker can decrypt the pre-master secret, calculate the master secret, and generate the session keys that were used for bulk data encryption.
DHE (Diffie-Helman Ephemeral Key Exchange) does provide forward secrecy because after the initial seed value is calculated, the Diffie-Helman parameters are discarded. All the values used to calculate the seed value are gone, and the seed value can’t be recreated. Hence DHE achieves forward secrecy.
RSA Key Exchange rates weak because it doesn’t provide forward secrecy.
ECDHE does and is rated STRONG.
Activate Certificate Transparency Monitoring
Cloudflare can monitor the Certificate Transparency Logs and notify you whenever a CA issues a certificate for your domain. If a CA mistakenly–or maliciously–issues a certificate for your domain, you’ll know.
Go to SSL/TLS and click on Edge Certificates, scroll down until you get to Certificate Transparency Monitoring, and activate it.
If you’d like to see a list of certificates that have already been issued for your domain in the past, visit https://crt.sh and enter your domain name. If you’re adding Certificate Authority Authorization DNS records for your domain as described in the next section, make sure have a look at crt.sh first to make sure you don’t miss any CAs.
Certificate Authority Authorization (CAA)
Any Certificate Authority (CA) can issue a certificate for any domain. CAs will do their due diligence and verify whether a certificate request comes from a domain owner or operator, but mistakes happen. CAA records allow you to specify which CAs are allowed to issue certificates for your domain.
However, it doesn’t shield you from rogue CAs, because the system relies on CAs checking your domain for CAA records and honoring them. But it does protect you against accidental mistakes by a CA that adheres to the CAA system.
Domains under Cloudflare free accounts–with Universal SSL enabled–can have their SSL certificates issued by any of the CAs Cloudflare uses, namely:
- Sectigo (formerly known as Comodo)
Cloudflare automatically adds records for the above CAs when you add your first CAA record to a domain with Universal SSL enabled. I’m only using Cloudflare’s CAs to issue certificates, so I only had to add the
iodef tag, and Cloudflare automatically added the other CAA
issuewild tags. The
iodef tag allows you to specify a URI where CAs can notify you about policy violations.
$ dig CAA netletic.com | grep -E '^netletic.com.' | cut -f1,6 | sort netletic.com. 0 iodef "mailto:email@example.com" netletic.com. 0 issue "comodoca.com" netletic.com. 0 issue "digicert.com; cansignhttpexchanges=yes" netletic.com. 0 issue "letsencrypt.org" netletic.com. 0 issuewild "comodoca.com" netletic.com. 0 issuewild "digicert.com; cansignhttpexchanges=yes" netletic.com. 0 issuewild "letsencrypt.org"
This means Sectigo, DigiCert, and LetsEncrypt can issue certificates for netletic.com, any subdomains, and *.netletic.com wildcards. It’s not as restrictive as I’d like it to be, but disabling Cloudflare’s Universal SSL is a paid feature.
HTTP Strict Transport Security (HSTS)
A user visiting your domain is vulnerable to a man-in-the-middle SSL stripping attack if the user visits your domain by using the HTTP version–http://example.org–and you redirect the user to the HTTPS version with a 301 Redirect.
If you activate HSTS with a max-age timer of 6 months, the user’s browser will remember this setting, and the browser will do a 307 Internal Redirect to the HTTPS version if the user attempts to revisit http://example.org within those 6 months.
Go to SSL/TLS and click on Edge Certificates, scroll down until you get to HTTP Strict Transport Security (HSTS), and click Change HSTS Settings. Enable HSTS and set the Max Age Header to 6 months.
I went a step further with my blog, set the max-age to 12 months, and activated preload. This allowed me to add netletic.com to the HSTS Preload list.