When a TLS (Transport Layer Security) certificate is assigned, there is a trust chain that is created to verify everyone from the root CA (Certificate Authority) to the actual website’s certificate.
The chain itself is not verified. A given system, for example a web browser, will consider a server’s certificate as valid because it can build a valid chain with all the signatures and matching names according to X.509 which starts with a root CA that the client already has and ends with the certificate to validate (the server’s certificate). When a website is changing its certificate, the rest of the chain remain the same.
Signatures don’t create trust, they transport trust. It still has to start somewhere. Each client (either the browser or in the operating system) comes with a list of trusted CAs. These are the public keys and names of some entities which are deemed trustworthy. The user doesn’t choose them, the operating system or the browser come pre-loaded with these. The root CA are trustworthy if you trust them for not issuing certificates with fake information. When a CA signs a certificate for some entity, it is supposed to make sure that the public key that the CA puts in that certificate, along with the entity’s name, is really owned by that entity. Similarly, when a root CA delegates its power to another CA (intermediate CA), it makes sure through audits and binding contracts that the sub CA is trustworthy and that it will apply the same rules.
The Public Key Infrastructure relies on the client to know a priori a handful of public keys owned by trusted CA and that they implicitly trust everything that these CA signs. The certificates assume a tree-like structure, with the root and sub CAs as the tree and the end-entities, the TSL servers certificates as leaves. A certificate chain is a path from the root to a given leaf.
If a root or sub CA becomes untrustworthy a process known as revocation is triggered.
When installing a proxy (think Burp or ZAP) to intercept TLS traffic, the pentester (or the attacker!?) exports the proxy certificate and installs it as a root CA on the target system. All the TLS certificates will be signed by this new root CA.
Before the new root CA is installed, the browser will display information like this
After the new root CA is installed, it starts signing all the certificates and the browser will report the following
In the example above (PortSwigger’s Burp certificate was installed on the target system) this happens because each CA can create any certificates they want, for example they can create a certificate for google.com even if there is already such a certificate from another CA. And the browser will accept these certificates because they trust the root CA.
Now, mobile apps have a particular way of protecting against this. In a simplified scenario it works something like this: the client makes a connection to the server and the server responds with its TSL certificate. If that certificate was issued by a Certificate Authority that is trusted by the OS, then the connection is allowed. All data sent through this connection is then encrypted with the server’s public key. For an attacker’s perspective, the mobile device would have to trust the attacker’s certificate. Through phishing, physical access or other means an attacker can push a CA certificate on the device and thus be able to perform man in the middle attacks.
Certificate pinning to the rescue
Certificate pinning is making sure the client checks the server’s certificate against a known copy hard-coded in the application of that certificate and not against the OS’s trusted CAs. Simply bundle your server’s certificate inside your application, and make sure any TLS request first validates that the server’s certificate exactly matches the bundle’s certificate. A good article on the technical bits of certificate pinning implementation can be found on OWASP’s web site.
The problem of multiple end-points
A mobile application can connect to multiple backend services. Multiple endpoints means multiple public certificates that need pinning. For a handful it might be manageable but if the number increases it’s advisable to look for another solution. Creating a unique endpoint that acts as a proxy and a load balancer for all the requests might be a feasible solution and would require just one pinned certificate.
As additional supporting material and refresher, I propose the following: