HTTP Public Key Pinning

Every now and then you hear an abbreviation of a new technology. Today: HPKP which stands for HTTP Public Key Pinning. It's an IETF standard that became final this month.

HTTP Public Key Pinning (HPKP) is an HTTP extension and security policy which can be set through HTTP response headers, just like HSTS (HTTP Strict Transport Security). It gives a website the possibility to instruct the browser to check for a specific public key when the website is visited the next time. When a user visits the website the next time, the browser checks the public key of the certificate of the website and it will compare it to the public key received in the HPKP header during the first visit. This measure could protect against Man-In-The-Middle (MitM) attacks. When HPKP is not used, MitM attacks are possible when for example a Certificate Authority (CA) is compromised and false certificates have been issued. Adversaries can use a false certificate to perform a MitM attack, because browsers think this false certificate has been issued by a trusted party. HPKP can protect against these kind of attacks, because of the before mentioned public key check.

HPKP is an IETF standard: https://tools.ietf.org/html/rfc7469

According to the standard HPKP is designed to be used together with HSTS. But is also possible to use only one of both extensions.

The following browsers support HPKP at this moment: Chrome 38+ and Firefox 35+.

How does it work?
A website can enable HPKP by setting the following HTTP response header:

[code language="text" gutter="false"]
Public-Key-Pins: pin-sha256="base64key1=="; pin-sha256="base64key2=="; max-age=expireTime
[; includeSubdomains] [; report-uri="reportURI"]
[/code]

This example contains two pin-sha256 attributes. These attributes contain a base64 fingerprint of the SPKI (Subject Public Key Information) associated with a certificate or keypair. It’s not just the public key, but the SPKI; which is the public key including the algorithm that has been used. In this blog I tend to use the term public key, with which I refer to SPKI. For more information about SPKI see:
http://en.wikipedia.org/wiki/X.509#Sample_X.509_certificates
https://tools.ietf.org/html/rfc5280#section-4.1.2.7

It is possible to include a public key of a production certificate and a backup certificate (it’s even possible to use more than one backup).

When a browser receives a HPKP header, it will save the information from the header in a pinned-host-cache. For each next visit of a specific website, the browser will check if one of the pinned certificates is present in the certificate chain that is presented by the website (or MitM). When this is the case, it’s a valid situation. If not, the connection will be disconnected and the browser prevents the user from visiting the website. When a report-uri attribute is configured in the HPKP header, the browser will send a notification about this event to this uri (a HTTP POST in json format).

The mechanism is a so called trust-on-first-use (TOFU), just like with HSTS. A user must have visited the legitimate version of a website before, before this measure takes effect. This measure is useless when a user is confronted with a MitM attack when the user didn’t visit the HPKP enabled website before. An adversary can also provide his own HPKP header during a MitM attack. To prevent against these kind of attacks, it is possible to get your website on the pre-loaded-lists of browsers.

It’s important to know that HPKP is all about public key pinning and not about certificate pinning. A public key belongs to a specific private key (together the keypair) and it does not contain an expiration date by itself. A certificate on the other hand does have an expiration date. And that’s exactly why public keys are pinned instead of certificates.

The max-age attribute is the expiration time in seconds. includeSubdomains can be added when the HPKP header also applies to subdomains.

Testing can be easily done by using the report-only header. The header than looks like this:

[code language="text" gutter="false"]
Public-Key-Pins-Report-Only: pin-sha256="base64key1=="; pin-sha256="base64key2==";
report-uri="reportURI"
[/code]

With this header it's possible to test your setup, without the policy being enforced. The max-age attribute is not applicable in this report-only header.

Tactics and considerations
Because the HPKP-check in browsers will check the leaf certificate and the whole certificate chain, you can choose to include the public key of your server certificate in de HPKP header or the public key of the CA or intermediate certificate.

When a CA is compromised, an adversary can issue a certificate and use it in a MitM attack. When a website does have a HPKP-pin based on the public key of the server, the MitM attack will fail. The browser after all accepts no other public keys than configured in the HPKP header. So, this is a strong argument for using the public key of your server certificate in the HPKP header.

However, when the website itself has been compromised, the server certificate must be revoked and then it's game over! That’s the reason why the backup pin is mandatory. A backup pin will be your rescue when your website has been compromised. For a backup pin you don’t have to request a certificate, you only need a valid keypair (public+private key). Whenever needed, you can request a certificate based on this backup keypair. Pay attention where to save your backup keypair; and that is not on your production server! When an adversary obtains your backup keypair, he can perform MitM attacks silently.

Including the public key of your CA in the HPKP header can also be a good idea. It gives you the opportunity to change your servers public key (keypair and/or certificate) without changing the HPKP header.

So, you have to choose wether you are going to use the public key of your server certificate or the public key of the CA’s certificate. Many organisations choose to use the public key of the server, including a backup pin and a good procedure. The IETF standard itself advices to use the public key of the issuer: https://tools.ietf.org/html/rfc7469#section-4

When including the public key of your server certificate in the HPKP header, there’s no need to update your HPKP header every year when renewing your certificate. You can request a new certificate based on the same keypair. But that’s a choice as well!

Misuse: super-cookie
This is not a vulnerability in the protocol, but a form of misuse some websites tend to use. HPKP can be used as a super-cookie. In the IETF standard it is described as follows:

Hosts can use HSTS or HPKP as a "super-cookie", by setting distinct policies for a number of subdomains. For example, assume example.com wishes to track distinct UAs (editors note: User Agents) without explicitly setting a cookie, or that a previously set cookie is deleted from the UA's cookie store.

example.com can use report-uri and the ability to pin arbitrary identifiers to distinguish UAs.

  1. example.com sets a Valid Pinning Header in its response to requests. The header asserts the includeSubDomains directive and specifies a report-uri directive as well. Pages served by the host also include references to subresource https://bad.example.com/foo.png.

  2. The Valid Pinning Header includes a "pin" that is not really the hash of an SPKI but is instead an arbitrary distinguishing string sent only in response to a particular request. For each request, the host creates a new, distinct distinguishing string and sets it as if it were a pin.

  3. The certificate chain served by bad.example.com does not pass Pin Validation given the pin set the host asserted in step (1). The HPKP-conforming UA attempts to report the Pin Validation failure to the specified report-uri, including the certificate chain it observed and the SPKI hashes it expected to see. Among the SPKI hashes is the distinguishing string in step (2).

More forms of using HPKP as a super-cookie can be found in the standard: https://tools.ietf.org/html/rfc7469#section-5

Summary
The IETF standard summarises HPKP as follows:

Thus, key pinning as described in this document is not a perfect defense against MITM attackers capable of passing certificate chain validation procedures -- nothing short of pre-shared keys can be. However, it provides significant value by allowing host operators to limit the number of certification authorities that can vouch for the host's identity, and allows UAs to detect in-process MITM attacks after the initial communication.

Just like years ago with HSTS, HPKP is not fully supported by all browsers. Still it is a nice measure which can help to prevent against MitM attacks. When implementing HPKP, you have to make some important decisions like the ones described in the ‘Tactics and considerations’ part above. Testing and having a good backup procedure are vital, because whenever something goes wrong users cannot visit your website anymore!

Do you want to be prepared for a MitM attack and be prepared for the case of a CA-compromise? Then enable HPKP together with HSTS for your website!

References
https://tools.ietf.org/html/rfc7469
https://timtaubert.de/blog/2014/10/deploying-tls-the-hard-way/#hpkp
https://timtaubert.de/blog/2014/10/http-public-key-pinning-explained/
https://developer.mozilla.org/en-US/docs/Web/Security/Public_Key_Pinning
http://www.exploresecurity.com/five-considerations-for-http-public-key-pinning-hpkp/
https://www.imperialviolet.org/2011/05/04/pinning.html
https://scotthelme.co.uk/hpkp-http-public-key-pinning/
https://vcsjones.com/2015/02/23/public-key-pinning/