I decided to take it as a challenge to get a full perfect score on the de facto standard of SSL implementation quality, the Qualys SSL Labs Server Test.

Needless to say, getting a perfect score is not without cost. For example, many browsers will be incapable of accessing the site. For this reason, I decided use a “disposable” domain name: ssl100.quantum2.xyz, which also runs on a separate IPv6 address to prevent any contamination on this website (there is no IPv4 since I didn’t have a disposable address), so you will need IPv6 access.

A screenshot of Qualys SSL Labs Server Test showing ssl100.quantum2.xyz getting a full score.

Incidentally, this also gets an A+ on securityheaders.io.

This is the configuration I used. Think thrice before copying, since it’s rather extreme, and will break your site for some users.

ssl_certificate         /etc/letsencrypt/live/ssl100.quantum2.xyz/fullchain.pem;
ssl_certificate_key     /etc/letsencrypt/live/ssl100.quantum2.xyz/privkey.pem;
ssl_trusted_certificate /etc/letsencrypt/live/ssl100.quantum2.xyz/chain.pem;

ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
ssl_ciphers "EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH:!AES128:!SHA";
ssl_ecdh_curve secp384r1;
ssl_dhparam /etc/nginx/dhparam4096.pem;
ssl_session_cache   shared:SSL:4m;
ssl_session_timeout 1h;
ssl_stapling on;
ssl_stapling_verify on;
resolver 8.8.8.8 8.8.4.4;

add_header Public-Key-Pins 'pin-sha256="YLh1dUR9y6Kja30RrAn7JKnbQG/uEtLMkBgFF2Fuihg="; pin-sha256="sRHdihwgkaib1P1gxX8HFszlD+7/gTfNvuAybgLPNis="; pin-sha256="C5+lpZ7tcVwmwQIMcRtPbsQtWLABXhQzejna0wHFr8M="; pin-sha256="Vjs8r4z+80wjNcr1YKepWQboSIRi63WsWXhIMN+eWys="; max-age=2592000' always;
add_header Strict-Transport-Security max-age=31536000 always;
add_header Content-Security-Policy "default-src 'self'" always;
add_header X-Frame-Options SAMEORIGIN always;
add_header X-XSS-Protection "1; mode=block" always;
add_header X-Content-Type-Options nosniff always;
add_header Referrer-Policy strict-origin-when-cross-origin always;

Effectively, this configuration allows only TLSv1.2 (while TLSv1.3 is listed, the stack does not support the same draft that the test uses). It prefers only AES256 ciphers, operating with either a DiffieHellman parameter ≥ 4096 bits or the elliptic curve equivalent on the secp384r1 curve, equivalent to 7680 bits. I also had to get Let’s Encrypt to issue a 4096 bit RSA certificate, with OCSP must-staple thrown in as a bonus. Naturally, OCSP stapling is included in the configuration.

The security headers are fairly standard, and securityheaders.io has a fairly decent explanation for all of them.