As we move into an more secure environment, simply have HTTPS isn’t sufficient. Many cases of forged SSL certificates for man-in-the-middle attacks have appeared recently.
These can be obtained through deception or hacking attemps on SSL CAs.
One method to help combat this is HTTP Public Key Pinning (HPKP) – this is where the webserver can communicate to the web browser an allowed certificate path, with a sufficiently long expiry time to be of use to return visitors. It’s not perfect or ideal, but it’s better than nothing.
This post does NOT detail the most secure method of HPKP but a compromise in terms of usability vs security. It will ensure that an unauthorised certificate would need to have been generated by one of your chosen SSL CAs only. The more secure method of having dual online and offline keys is out of scope for this blog post and a more advanced topic.
The way this works is to publish a header in the HTTPS response detailing the allowed hashes of public keys of webserver keys or keysigner keys that are permitted to be in the certificate chain. This is in addition to the normal process of validation of the full SSL certificate chain.
BEWARE: misconfiguration of this header can lead people to be unable to view your site for MONTHS so be careful!
First let’s look how to obtain the SHA-256 hash needed for a certificate. We use OpenSSL to do this based on the .crt file… Let’s assume the certificate is in the cert.crt file…
openssl x509 -pubkey < cert.crt | openssl pkey -pubin -outform der | openssl dgst -sha256 -binary| openssl enc -base64
This will provide a Base64 encoded string which we can use later.
Now we provide the information to the webserver… i’ll detail Apache 2.4 here, but other servers will have similar methods of adding header outputs. We add the following into the VirtualHost block for the SSL site:
Header always set Public-Key-Pins “pin-sha256=\”HASH\”; pin-sha256=\”HASH\”; max-age=30;”
where the Base64 has generated previously replaces the first ‘HASH’.
HPKP requires a minimum of 2 hashes to be present, including one hash that is NOT present in the certificate chain. This is to encourage you to ensure you have a backup plan.
In my examples, I use the hash of my SSL public key and the hashes of my chosen primary and secondary SSL certificate authority’s intermediate certificates.
In my case, I use StartSSL’s class 2 intermediate and RapidSSL’s SHA-2 under SHA-2 root intermediate. To generate their hashes, use the above OpenSSL method on their CA certificates.
This means that I can use my existing key/csr with any SSL CA, or I can generate a new key with my primary or secondary CA. It also satisfies the requirement for one of the hashes to not exist (my current certificate is not present via my secondary CA)
The max-age above is set to 30 seconds for testing purposes… once you’re completely happy with your choice, this should be raised to a much longer figure. I use a 30 day period (2592000 seconds)
A good method of testing is to use Qualys SSLlabs tester at https://www.ssllabs.com/ssltest/ – this will show you the pinned list, including any currently in the chain in a different colour.