Understanding Certificate Pinning

Certificate pinning (“cert pinning” for short) is a technique used for mobile applications to add an extra layer of protection to secure communications. Some people additionally use the technique to prevent people from reverse engineering APIs via intercepting proxies, however this latter objective is hard to achieve against a determined hacker.

Certificate pinning offers very high security, but it does come with some downsides that need to be considered by the business. This blog explains the security and business considerations for certificate pinning, and shows the trade-offs that can be made according to the need of the organisation implementing it.

The takeaways from this blog are:

  • Understanding why we do cert pinning
  • Understanding the public key infrastructure (PKI)
  • Making an analogy with browser security
  • Certificate pinning typically comes at the cost of forced mobile app upgrades, which can be a particularly painful user experience for short-live certificates such as those provided by Let’s Encrypt
  • Apps that are cert pinned need to roll out new versions in coordination with operations teams that rotate certificates because upgrading an app requires time (including AppStore review) — otherwise there will be downtime
  • The option of Certificate Authority Pinning is less strict than full Certificate Pinning. It requires less maintenance while offering slightly less but still strong security

What is Cert Pinning and Why Would I Use it?

In order to understand certificate pinning, one must understand PKI. An excellent blog on this is provided by Technophile: SSL/TLS for dummies part 3 – Understanding Certificate Authority — embarrassingly, their certificate is expired at the time of writing, so just read below.

The short summary is that secure communication over the internet is done via TLS (often people wrongly say “SSL”, which was a predecessor of TLS that was deprecated due to security weaknesses). The security of TLS depends upon a small set of organisations called certificate authorities (CAs), whose role is to verify the identity and then issue certificates to websites, which allow them to use TLS security. To make this all work, operating systems and potentially other software providers need to be supplied with a small set of certificates from the certificate authorities — these certificates are the “root level” certificates that the everything else in TLS depends upon. When you visit a website via TLS, the certificate provided by that website needs its signature checked against one of the root level CAs to verify its validity — this asserts that you are visiting the website that you think you are visiting and communication is secure to that website, assuming the CAs have not been breached and the OS/software vendor has not been breached and you do not have a bogus CA installed and the website you are visiting has adequately protected its private cryptographic keys. If any of those assumptions are wrong, all bets are off.

As mentioned, there are many ways that TLS can break, but the most severe would be if a certificate authority is breached since it affects everybody. Such security incidents have happened, the most prominent example being DigiNotar.  Less than having a CA breach, there are other examples where PKI security shortcomings have affected a large number of people, such as the Lenovo Superfish incident.

It must be emphasised that if any single CA is breached, then all of TLS breaks. It doesn’t matter what CA you used for your website, it only matters what CA your attacker uses. I’ve seen major organisations that are supposed to be security savvy having polices of only getting certificates from a subset of CAs that they trust the most — they entirely miss the point that the CAs they trust don’t matter: the design of PKI means we all depend upon every single one of them not being breached.  At the end of this blog, we will show there is one scenario that can such policies meaningful: however I have yet to see it implemented in practice.

The fact that every CA needs to be completely secure for PKI to work is one reason that some people do not trust PKI. However, this is where certificate pinning comes to the rescue for mobile apps: the main reason to use it is to make up for the shortcoming of PKI. The concept of certificate pinning is to make the mobile app only trust the exact certificate that is used on the website that it is communicating with, and thus no longer depend upon the security of CAs. One way to implement this (other, more practical ways will be mentioned later) is to either provide the exact certificate (or else the cryptographic hash of it) in the mobile app source files during development and write code that will verify that this certificate matches what the website is sending during the initial stages of the TLS communication. If there is not an exact match, then reject the communications and do not proceed. This provides very strong security, but it does come with some potential gotchas that we will explain later.

There is a second reason why some people use certificate pinning, which is to prevent people from reverse engineering your mobile app via an intercepting proxy. Without certificate pinning, anybody can view the communications between their mobile app and the server using well known techniques (see here and here).   This is not breaking TLS — other people cannot inspect your communications, only you can inspect your own communications by installing an intercepting proxy certificate on your mobile device. By adding a new certificate that the OS trusts and for which you know the private key and nobody else does, you have made it so you can snoop on the TLS communications but nobody else can. This does not directly work if the certificate is pinned because the application stops the interception — it does not trust the newly installed certificate, but instead only trusts the exact certificate that is pinned.

However, this defence has limited efficacy because it is possible to bypass certificate pinning using well known techniques (see here and here), and because another approach to understanding how APIs work is the old-fashioned reverse engineering of the binary. As a consequence, to prevent adversaries from understanding your API, you also need obfuscation and jailbreak/root detection. Such defences will stop many hackers, but a skilled and determined hacker with a lot of free time can ultimately succeed.

Concluding, it makes sense to do certificate pinning to prevent potential PKI insecurities, but cert pinning does not stop people from understanding your APIs — it merely slows down the good hackers. For the rest of this blog, our primary focus is on the use of cert pinning to deal with potential PKI problems. Cert pinning to slow down reverse engineering is outside the scope of this blog.

If a CA becomes breached, the manufacturer should push a security update

OS vendors / mobile device manufacturers have a strong interest in ensuring security: people trusting in their products is important to their business success. As a consequence, they usually push security updates to their customers when a CA becomes no longer trusted. For example, Apple released an update when DigiNotar was compromised. Similar updates were done by browser manufacturers as well as described here.

So this gives us some assurance that even without certificate pinning, we can still have decent security.  However there are some catches:

  1. Users don’t always install their security updates promptly, so there is gap when exploitation is possible.
  2. This only works for known compromises — whereas certificate pinning will protect against the unknown compromises.
  3. The situation for Android is less assuring because the security updates come from Google via the mobile device vendor, and the vendors have not always been so prompt in pushing security updates to their users.

Related to (3), Google does provide a solution for app developers to protect clients against discovered TLS vulnerabilities without relying on security updates from the vendor by using API installIfNeeded() or installIfNeededAsync().  It’s not clear to me from the documentation whether this fixes only TLS implementation bugs, or if it also updates root certificates on the device.

So, while there is some fallback, there is definitely room for improving security with a technique such as certificate pinning. However there is a practical downside to certificate pinning that one needs to be aware of — the app breaks when the certificate changes. We will discuss that more later.

Comparison to Browser Based Security

It’s interesting that we mandate such a high standard for mobile security, but what about the corresponding web browser security? Our ability to have similar controls is somewhat restricted, so one might feel that we are treating the mobile app more holy than the browser.

In 2015, the security community tried to compensate for the discrepancy between mobile security guidance and the web browser security capabilities by introducing a similar concept to the web browser. A new http header was added that allowed websites to instruct browsers to pin their certificates, so the browser would not accept any other certificate that it was presented from that particular website (for the duration of the validity specified in the header). This was known as HTTP Public Key Pinning (HPKP).

It didn’t take long before the security community discovered problems with this proposal. Within 2 years, one of the authors of HPKP announced the intent to deprecate it due to a number of problems that were not previously anticipated. The reasons for deprecation include the difficulty in choosing a set of pins that is guaranteed to work, the risk of locking users out if an error was made in pinning, and the risk of an attacker abusing HKPK if he could obtain a misissued certificate. A real example of locking out users is here. With the deprecation, A replacement was recommended : Expect-CT header. Expect-CT header is formally documented here.

Expect-CT is not as strong as certificate pinning, but is safer to use (still, the authors suggest using it with caution). In a nutshell, it is used to instruct browsers that the website must have a published certificate, in compliance with Google’s Certificate Transparency effort. With some configuration, the browser can be instructed to block connections if the site does not comply. Thus if a malicious actor secretly got an unpublished, misissued certificate for a website he would not be able to abuse it with this website. In other words, for the malicious actor to abuse a misissued certificate, it needs to be one that is published! But hopefully those abuses can be caught by certificate revocation (or maybe not).

Expect-CT is not supported by a number of browsers (including Firefox) at the time of writing this blog. It is a big step towards fixing PKI shortcomings, but falls slightly behind the security one gets from mobile certificate pinning.

Implementation and Caveats

The company Infinium has written excellent blogs for developers on how to implement certificate pinning. These guides can be found here:

A key downside of certificate pinning, which needs emphasis, is that changing certificates implies that you need to release a new version of the app (with an exception, to be discussed below) and force upgrade your users to the latest version. This is because the app only works with the exact certificate that was pinned — communication breaks if any other certificate is used. If you are using short-lived certificates, such as those from Let’s Encrypt, then this requires frequent maintenance and is an unpleasant user experience.

Another points is that the rotation of the certificate will need to be coordinated with the release of the updated app to avoid downtime. This means that the development team will need sufficient advanced notice to update the app with the new certificate, test it and review it, and get it through App Store review, or else users may be locked out for some period of time. This is a very real concern: I’ve seen it happen, and the business was not happy.

The exception mentioned above is that if only the public keys (as opposed to the full certificate) are pinned then one can rotate a certificate without releasing a new app provided that the same public keys are used in the new certificate. That is, you change the certificate when it expires but use the same cryptographic keys as before. This results in a more friendly user experience, but it requires those that manage certificates (typically an operations team) to follow the requirement from the development team. Of course, if any key changes, a new app needs to be released and a forced upgrade needs to happen.

There is another option that offers slightly less security than full certificate pinning but
is much more likely to avoid the problem of needing to release a new app and force upgrade the users…

Alternative Option: Certificate Authority Pinning

With the goal of eliminating the need to upgrade the app when a new certificate is released but still wanting better-than-TLS security, developers can pin the certificate authority public key only. In other words, the app will now only accept certificates that are signed by the specific CA (or CAs) that you allow and no others, in addition to all the normal certificate checks that are required for security.

Recall our discussion above about the problems with PKI: if any CA is breached, then everybody becomes vulnerable. However, by pinning the certificate of the CA or CAs that we trust, we no longer have that risk. Instead, the exact CAs that we trust need to be compromised for us to be vulnerable.

This is not as strong as full certificate pinning because if an intermediate key within your chain of trust becomes compromised, then you are still vulnerable. However, it is better than TLS-only because now you do not depend upon all CAs being secure: instead you only depend upon the CA or CAs that you are using being secure.

With this approach, the app only needs to be updated if you change CAs or if the CA changes its public key. If your company has a policy on which CAs they will restrict to, then you can pin the public keys of those exact CAs and quite likely you will not need to worry about updating the app or forced upgrades for a long, long time. This is especially appealing for those using short-lived certificates.

For Android devices, pinning the CA public key with the TrustManager
(see also this).

For iOS, certificate authority pinning is discussed here.