The foundation of trust upon which all web commerce depends begins with the browser. The browser trusts certificate authorities (CAs) and certificate authorities vouch for the identities of businesses. This system of public key infrastructure (PKI), however, is only as strong as the weakest certificate authority. If a trusted CA gets compromised, an attacker can generate rogue certificates, impersonate a site, and pull off a man-in-the-middle (MitM) attack. To reduce this risk, the IETF has proposed a new standard for HTTP public key pinning, HPKP, which is already implemented in Chrome, Firefox, and Opera.
According to the standard, an HPKP header instructs browsers to link a domain name to a restricted set of public keys. Based on this pinning, the browser should block any future content if the server’s certificate chain does not contain a matching public key. This protects all returning users but not first time visitors, since first time visitors would not yet have received the HPKP header. While not perfect, this trust-on-first-use (TOFU) approach is as good as you can get without actually exchanging keys in advance.
Here’s an example of an HPKP header (with line breaks inserted to aid readability):
Public-Key-Pins:
pin-sha256=”lb8Ox4AhFuA6Bg1GqqwQSgrTx6qdYXQjhUf4b+MAJPo=”; pin-sha256=”7HIpactkIAq2Y49orFOOQKurWxmmSFZhBCoQYcRhJ3Y=”;max-age=5184000;
includeSubdomains;
report-uri=”https://www.mydomain.com/myreporturl”
Let’s examine this header one directive at a time.
pin-sha256
The mandatory directive pin-sha256 contains a Subject Public Key Information (SPKI) fingerprint. The standard defines this fingerprint as a base64-encoded SHA-256 hash of the SPKI section of a DER-encoded certificate. While the definition is quite a mouthful, the values are easily generated using the open-source command line tool openssl, which can be installed on Windows, Mac, and Linux.
openssl x509 -in mycert.crt -pubkey -noout | openssl rsa -pubin -outform der | openssl dgst -sha256 -binary | openssl enc -base64
This command in turn extracts the public key from mycert.crt, converts it into the binary DER format, takes the SHA-256 digest of the public key, and encodes that digest into Base64. The output of this command can be copied into the HPKP header.
Note that the SPKI section includes a certificate’s public key but no data unique to the certificate. This enables site owners to generate new certificates using the same public key such that the new certificate matches the pinning. This is critical since otherwise users protected by the pinning would lose access to your site if the original certificate were revoked.
The standard supports pinning either your site’s certificate or any certificate in the hierarchy between your certificate and the CA’s certificate. Pinning your site’s certificate provides greater security as it will protect you even if the root CA is compromised.
max-age
The mandatory max-age directive indicates in seconds how long the browser will enforce the pinning. The value represents a time-to-live that gets updated each time the browser processes a page with the HPKP header. A longer value reduces the risk of MitM attacks that could be exploited whenever the pinning expires; a shorter value reduces the risk of locking out users if something goes wrong with the certificate. The standard recommends a value of 60 days (5,184,000 seconds) as a reasonable balance.
Setting the value to zero causes the browser to remove the pinning information. The standard does not define a maximum value but leaves it up to browser vendors to do so.
includeSubdomains
The optional directive includeSubdomains applies the certificate pinning to all subdomains of whatever domain issues the header. Use this directive with care. Pinning subdomains could block access to subdomains that have distinct certificates not included in the pinning.
report-uri
And optional report-uri informs the browser to post reports of violations in JSON format to a specific path. The standard also supports an alternative report-only version of the HPKP header, Public-Key-Pins-Report-Only, which tells the browser not to block access but just report the violation.
Backup Pins
To reduce the risk of access denial, the HPKP standard requires at least two pin-sha256 values in a header. The concern is that if your private key were compromised or certificate revoked, no user who had visited your site would be able to visit again until the pinning expired. Fortunately, you do not need to buy backup certificates to create backup pin-sha256 values because openssl can create SPKI fingerprints from certificate signing requests:
openssl req -in myreq.csr -pubkey -noout | openssl rsa -pubin -outform der | openssl dgst -sha256 -binary | openssl enc -base64
Regarding backup keys, there are a few points to keep in mind:
- Do not rely on a certificate in the same chain as a backup.
- Do not to rely on a CA’s root certificate or intermediate certificate as a backup unless you can be sure to get your backup CSR signed by the private key behind that same certificate.
- It’s best to rely on what you can control and hence use your own CSRs as a backup.
- Use a separate public-private key pair to create the backup.
- Keep the keys offline and secure. Don’t lose the backup.
Intercepting Proxies and Limitations of HPKP
In addition to shipping with a set of trusted root certificates, browsers permit users to install additional root certificates. Several types of software make use of this capability to intercept TLS traffic. These include anti-virus filters, parental controls, ad-blockers, and HTTP proxy tools such as Fiddler and Charles. These tools install root certificates that they use to generate certificates on the fly for each HTTPS request, intercept that request, and proxy it to the origin.
This technique can serve useful purposes, but it does create risk when implemented poorly. In the Superfish fiasco, Lenovo made the mistake of installing the same root certificate on many user machines together with the private key, making it possible for an attacker to use the certificate to launch a MitM attack against anybody with the certificate installed. Quite recently, Dell made this same mistake. In addition to such obvious certificate errors, makers of these products have made other errors in implementing TLS. PrivDog, an ad blocker, erroneously accepted invalid certificates. Others have implemented TLS in ways that enable attackers to downgrade TLS encryption levels. (See Hanno Böck for details.)
It certainly would be nice for HPKP to protect us from such folly, but as Chris Palmer argues, it is just not feasible. Consumers have chosen to install these products, an indication that consumers want the benefits they offer. Moreover, no user-level program such as a browser can protect us if our computer has been compromised by the installation of malicious or negligent code. Therefore, the HPKP standard calls for browsers to bypass HPKP checks when presented with a certificate signed by a locally installed root certificate. So while HPKP protects against rogue certificates out on Internet, it does not protect against those installed on our own computers.
Conclusion
Despite its unavoidable limitations, HPKP offers a valuable protection for users of supported browsers and is relatively easy to implement as long as back-up keys are properly managed. Why not take this simple step to close off an attack vector?
Resources
IETF HPKP Standard | https://tools.ietf.org/html/rfc7469 |
Browser Support | http://caniuse.com/#feat=publickeypinning |
MDN Documentation | https://developer.mozilla.org/en-US/docs/Web/Security/Public_Key_Pinning |
OWASP Summary | https://www.owasp.org/index.php/Certificate_and_Public_Key_Pinning |
Reporting Tool for HPKP and CSP | https://report-uri.io/ |
Tool for Analysing a Site’s HPKP Policy | https://report-uri.io/home/pkp_analyse |
Generate pin-sha256 Value for a Web Site | https://report-uri.io/home/pkp_hash |
Leave a Reply