Can certificate pinning save us from the man in the middle?
With my COMODO-signed SSL certificate about to expire, I took the plunge and configured all my domains with certificates from Let’s Encrypt. I’ve tested it for a while on vbox4.vegard.net and it works well. The only downside is that it has to be renewed every third month, and that’s not an automated process. At least it’s not out of the box, but I’m sure it’s possible to set something up with a script and a Cron job - I’ve just not been down that avenue yet.
To be honest, I’m still a wee bit skeptical about a system where two parties rely on a implicitly trusted third party - in this case Let’s Encrypt - to communicate securely. As I pointed out in the post I wrote about Let’s Encrypt last year, you can’t be entirely sure that no one is playing around with the private root key, making their own certificates for your domains. With their own certificates for your domains, signed with the private root key from Let’s Encrypt1, someone could perform a man-in-the-middle attack without anyone noticing.
But there is a technique slowly being adopted by modern browsers that can, if not make it impossible to perform a man-in-the-middle attack against a website, at least make it a lot harder: HTTP Public Key Pinning, or, as it’s more commonly know as; certificate pinning.
With certificate pinning, a web server can explicitly tell a client exactly which certificates to trust when visiting the websites it hosts. This means that, even if someone somehow manages to create a technically valid certificate for your site, they will not be able to use it in a man-in-the-middle-attack because the hash of the certificate used by the attacker doesn’t match the hash the web server previously told the visitor the certificate should have.
In theory, this sounds like a great idea, but it’s not without flaws. The word “previously” in the above paragraph leads to one of them: If an attacker has already compromised a site when a user first visits it, the certificate pinning hash might have been altered to match the attacker’s fraudulent certificate. The visitor’s web browser will then treat that certificate as trusted on every subsequent visit to the site.
A solution to that problem would be to add trusted certificate hashes when the web browser is developed, i.e. out-of-band. Adding trusted hashes that way means the browser already knows what certificates to trust before its first visit to a particular site. But this solution is impractical because you can’t have the trusted hashes for every site on the internet shipped with a web browser.
Another issue with certificate pinning is that it’s outright dangerous if not done correctly. If you pin a certificate that suddenly stops validating, e.g. the pinning of a root certificate that is revoked because a CA fails to properly secure their private root key, web browsers relying on the pinned root certificate will no longer be able to connect to your site. Also, pinning a certificate with a longer max-age value for the hash than the validity of the certificate itself will mean that you effectively block your own site when the certificate expires2.
While certificate pinning is a step in the right direction to make the web more secure, it’s currently a bit too much hassle for me to start using it, particularly since I’m using Let’s Encrypt and the its three month certificate expiration. I’d at least need an automated system that updates the certificates and the corresponding hashes every three months.
But if you’re a financial institution, online shop or something similar that handles money and confidential information, you should start using certificate pinning today to make it harder for someone to launch a man-in-the-middle attack against your users and your business.
- Wikipedia: [HTTP Public Key Pinning](https://en.wikipedia.org/wiki/HTTP_Public_Key_Pinning)
- OWASP: [Certificate and Public Key Pinning](https://www.owasp.org/index.php/Certificate_and_Public_Key_Pinning)
- Noncombatant: [About Public Key Pinning](https://noncombatant.org/2015/05/01/about-http-public-key-pinning/)
Someone wanting to create a false certificate for your side wouldn’t need Let’s Encrypt’s private root key specifically, a root key from any of the certificate authorities that are trusted by your browser and operating system would work. ↩︎
To let a certificate expire is in itself a huge error, though. ↩︎