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.
The best resources that I've found for explaining these steps are https://https.cio.gov , https://certificate-transparency.org , and https://twitter.com/konklone
As I research and document this process, I'm starting a Python CLI OverEncrypt to perform these steps or check them automatically.
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.
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
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
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.
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'
(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.
In the 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.