From B to A+ at Qualys

Few days ago I checked this website with Qualys SSLlabs test tool and it scored merely a B, so I decided to raise it.

qualys score B

There were two main issues to get rid of, the lack of forward secrecy and the obsolete TLS versions.

Setting up Forward Secrecy

Forward Secrecy is a technique that allows the use of ephemeral session keys beside the long term keys (private keys) in order to prevent the decryption of stored traffic if the private key is compromised.

Nowadays almost all browsers and servers support it, and it’s very straightforward to set up.

First thing you need is a Diffie-Helman group.

$ sudo openssl dhparam -out /etc/pki/tls/certs/dhparam.pem 2048

I actually used 4096 entropy bits, but it took a lot to be generated, but 2048 is strong enough.

Then I added the following line to the nginx’s site configuration file.

ssl_dhparam /etc/pki/tls/certs/dhparam.pem;

After checking the configuration was valid, I reloaded nginx:

$ sudo nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
$ sudo systemctl restart nginx

Protocols support

In order to improve the score at Protocol Support section, I had to disable the support for TLS 1.0 and 1.1 and to enable the TLS 1.3 protocol.

This can be done easily on nginx using the ssl_procotols directive:

ssl_protocols TLSv1.3 TLSv1.2;

But I wanted to go one step beyond and be sure that the strongest ciphers were used, so I added two additional directives:

ssl_prefer_server_ciphers on;
ssl_ciphers "EECDH+ECDSA+AESGCM EECDH+aRSA+AESGCM EECDH+ECDSA+SHA384 EECDH+ECDSA+SHA256 EECDH+aRSA+SHA384 EECDH+aRSA+SHA256 EECDH+aRSA+RC4 EECDH EDH+aRSA RC4 !aNULL !eNULL !LOW !3DES !MD5 !EXP !PSK !SRP !DSS !RC4";

I gathered the cipher list from this forum discussion, note that being to strict on this can prevent older browsers to access the page, and according to the SSL Server Rating guide, the score is “best + worst / 2”. That makes scoring over 90% very hard (and restrictive).

Http Strict Transport Security (HSTS)

Other thing the test complained about was the lack of HSTS headers.

HSTS is a way for the website to tell the browser it’s preference to use HTTPS instead of HTTP, and as I’m using HTTPS and not even listening for plain HTTP connections, the idea sound great to me.

Again it is a piece of cake to set up in NGiNX, and also in Apache HTTPD, in my case:

add_header Strict-Transport-Security "max-age=31536000; preload; includeSubdomains" always;

This header tells browsers that the site, including its subdomains, has to be accessed by HTTPS, and that has to be remember for a year (31536000 seconds). The preload tag allows the site to be included in the HSTS preload lists.

The preload list is a hardcoded list integrated on Chrome and Firefox among other browsers that specifies which sites should be accessed only using HTTPS, in chrome, you can check it at chrome://net-internals/#hsts.

In order to request the inclusion on this list, the preload parameter must be present on the HSTS header, it’s free, but it comes with some caveats: the removal process is slow and obsolete browser versions will never update the list, and every subdomain must use HTTPS.

But if you are sure and committed to use HTTPS, you can ask for the inclusion of your website on the list at HSTS Preload page. I haven’t done it yet because subdomains are not allowed to ask for inclusion, but I’m pretty sure I’ll do it on the near future.

OCSP Stapling

OCSP Stapling is a step forward for the Online Certificate Status Protocol because it allows to reduce the effort of certificate status verification both for the clients and for the Certificate Authority.

It’s based on stapling a signed time-stamped OCSP response to the TLS handshake, it’s signed by the CA, so it’s easy for the browser to verify it against trusted CAs instead of contacting directly the CA. In this scenario is the server which contacts the CA at regular intervals, instead of every client for every https connection, so load in the CA is also reduced.

The directives to set up OCSP Stapling on NGiNX are:

    ssl_stapling on;
    ssl_stapling_verify on;
    resolver 8.8.4.4 8.8.8.8;

The first one activates stapling support, the second one instructs the nginx to verify the OCSP responses from the CA and the third one instructs nginx to use Google’s DNS servers to resolve the OCSP location.

Only the first one is mandatory, the second is optional as the client is going to perform the check itself, but prevents NGiNX to cache an invalid response. I used the third one for demonstration purposes, for instance it’s useful if your server is on a private network with non-recursive DNS servers and you want to contact other recursive DNS to resolve the OCSP server address.

CAA Record

The Certification Authority Authorization Record is a mechanism to tell the world, and CA authorities which one can issue certificates from a domain.

It was designed to prevent users to trust a certificate issued by a compromised CA and avoid man in the middle attacks, but it’s not widely used even though Microsoft and Google suffered this kind of issue, only the latter uses CAA records.

Setting up CAA requires a compatible DNS server, the current version of all major DNS servers are, but some providers don’t have the option on their management interfaces.

Luckily for me, Go Daddy has, so the first step was to generate the records, they are quite simple, but if you like using assistants, SSLMate CAA Record Helper is easy to use.

Once I entered my domain, selected my certificate provider and entering a mail address to contact in case of some violation was detected, the helper gave me a list of records to include in my DNS. CAA records

Then I had to enter those records on my dns management panel. At first glance, I though I was making it wrong, but it’s GoDaddy’s panel which was showing the information in reverse order. CAA records on GoDaddy CAA records on GoDaddy

Conclusion

Getting an A+ grade on Qualys test is not hard, it took less getting it than writing this post.

Qualys A+ grade

Although I’m not getting user’s data, not even by cookies, I advocate for network privacy so I went beyond the forward secrecy and tls versions requirements and set up HSTS header, the CAA records which I encourage everybody to use and also OCSP Stapling.

I was aware of OCSP, and heard about stapling, but I didn’t know what was exactly, and I discovered what were CAA records for a few days ago, but from now and on, I’ll try to use them as much as I’ll be allowed.

The whole configuration change

--- site.conf.bak	2020-12-06 11:45:30.160259944 +0100
+++ site.conf	2021-03-07 16:26:49.602846230 +0100
@@ -4,11 +4,22 @@
     listen 443 ssl http2;
     listen [::]:443 ssl http2;
 
+    ssl_protocols TLSv1.3 TLSv1.2;
+    ssl_prefer_server_ciphers on;
+    ssl_ciphers "EECDH+ECDSA+AESGCM EECDH+aRSA+AESGCM EECDH+ECDSA+SHA384 EECDH+ECDSA+SHA256 EECDH+aRSA+SHA384 EECDH+aRSA+SHA256 EECDH+aRSA+RC4 EECDH EDH+aRSA RC4 !aNULL !eNULL !LOW !3DES !MD5 !EXP !PSK !SRP !DSS !RC4";
+    ssl_stapling on;
+    ssl_stapling_verify on;
+    resolver 8.8.4.4 8.8.8.8;
+
     ssl_certificate /etc/letsencrypt/live/site/fullchain.pem;
     ssl_certificate_key /etc/letsencrypt/live/site/privkey.pem;
+    ssl_dhparam /etc/pki/tls/certs/dhparam.pem;
 
     server_name juanjo.garciaamaya.com;
 
+    # HSTS (31536000 seconds = 1 year)
+    add_header Strict-Transport-Security "max-age=31536000; preload; includeSubdomains" always;
+
     root /var/www/default;
 
     if ($ssl_protocol = "") {

References

Beside other directly linked on text.