This is a guide that I wrote to improve the default security of my website https://fortran.io , which has a certificate from LetsEncrypt. I'm choosing to improve HTTPS security and transparency without consideration for legacy browser support.
WARNING: if you mess up settings, lose your certificates, or decide to no longer maintain HTTPS certs, these steps can and will make your domain inaccessible.
I would recommend these steps only if you have a specific need for information security, privacy, and trust with your users, and/or maintain a separate secure.example.com domain which won't mess up your main site. If you've been thinking about hosting a site on Tor, then this might be a good option, too.
As I research and document this process, I'm starting a Python CLI OverEncrypt to perform these steps or check them automatically.
0. Before starting
Verify that your site with a LetsEncrypt certificate is accessible at https://example.com (for your domain).
You should be running the latest Apache or Nginx, and know the path to your fullchain.pem file.
If you got your first cert several months ago (even early 2016) you might want to check if your site is rated A+ or B on https://www.ssllabs.com/ssltest/index.html
If it's not renewal time, read through these steps, run
./letsencrypt-auto revoke --cert-path /path/to/cert, and do a git pull to update the LetsEncrypt / Certbot client. Then create a new cert in the same location.
1. OCSP Stapling / Must-Staple
LetsEncrypt recently added OCSP Stapling support to have browsers check for certificate revocations, but it does not require it with Must-Staple by default.
When you create a cert with
./letsencrypt-auto, add the option
--must-staple to enforce OCSP checks on the certificate level and not just as a header. I'm not sure if it works on renewal.
SSLMate has directions on https://sslmate.com/blog/post/ocsp_stapling_in_apache_and_nginx to configure Nginx or Apache to support OCSP. Most of the headers will be added already by
letsencrypt-auto. After you add the remaining ones, make sure to run
sudo service apache restart or
sudo service nginx restart
2. Public Key Pinning (HPKP)
You need to get SHA256 fingerprints of at least two certificates: your public cert key (which currently changes whenever you renew with LetsEncrypt) and the LetsEncrypt cert used to sign certificates (the cert which I listed here previously does not appear in new LetsEncrypt cert chains ... I will research the correct cert).
This can really mess things up if you switch away from LetsEncrypt or they change their cert, so look at these two first: https://community.letsencrypt.org/t/hpkp-best-practices-if-you-choose-to-implement/4625 and https://news.ycombinator.com/item?id=13074651 to see about adding others, or skipping this step if you're worried about locking people out.
openssl x509 -noout -in /path/to/cert.pem -pubkey | openssl asn1parse -noout -inform pem -out public.key openssl dgst -sha256 -binary public.key | openssl enc -base64 > aaaa111example
In your Nginx settings
add_header Public-Key-Pins 'pin-sha256="aaaa1111example"; pin-sha256="exampleRootCert"; max-age=2592000; includeSubDomains';
Settings for Apache and many other systems: https://raymii.org/s/articles/HTTP_Public_Key_Pinning_Extension_HPKP.html
3. Certificate Transparency
You can find LetsEncrypt's registered certificates for your site at https://crt.sh/
As best I can tell from https://certificate-transparency.org , this is as much as you need to do for now. Browsers will get smarter about this in the future.
4. Allow Preload
In the part of the cert where it reads
add_header Strict-Transport-Security, add 'preload' to the end of the header, e.g. in Nginx
add_header Strict-Transport-Security 'max-age=15768000; includeSubDomains; preload'
5. Prefer AES-256 to AES-128
(see @bwesterb comment below... this makes it harder to decrypt your traffic, but TLS key exchange is still a weak point)
This is a personal preference, but makes traffic more resistant to being broken by quantum computers, now or in a collect-now-read-everything-later dystopian future. AES-256 on a quantum computer remains as strong as AES-128 today on classical computers; AES-128 will be (or perhaps is?) broken. You can read more in my write-up of post-quantum encryption.
ssl_ciphers section, there is a list of ciphers in order by the server's preferred use, separated by :s. Also some are forbidden. I removed all mentions of AES128 from this list. The generic :AES: allows AES-128 queries unless it's removed, too.
In the future these steps might support Google's work on post-quantum encryption with BoringSSL / Ring Learning With Errors / A New Hope / Lattice-Based Encryption. But not yet. (this would fix the key exchange problem mentioned by @bwesterb)
Make sure you've run
sudo service apache restart or
sudo service nginx restart to use your new cert.
On the top of the SSL Labs test, click the 'Clear Cache' link to re-run tests.