Securing your mobile applications ensures that you and your customers are safe. And unfortunately, just using SSL and HTTPS doesn’t fully protect your data. Instead, certificate pinning currently tops the list of ways to make your application traffic secure.
Today we’ll look at what certificate pinning is and how it secures your application from man-in-the-middle attacks. We’ll compare certificate and public key pinning. And lastly, we’ll look at why leaving certificate pinning as a build flag makes your app flexible for man-in-the-middle testing.
Before we get into specifics, let’s take a look at the life of a request from your mobile app to its back-end server using HTTPS. Without looking into things, we only know that our client sends a request and the server sends a response. However, more subtlety exists in this exchange than many realize.
Before the client and server exchange application data, a figurative handshake occurs to confirm the client is talking to the right server. This handshake follows these five steps:
- The client initiates a handshake with the server and specifies a Transport Layer Security (TLS) version.
- The server responds with a certificate and public key.
- Then, the client verifies the certificate or public key and sends back a shared key. This shared key is based on the public key from the server.
- Next, the server confirms receipt of the shared key.
- And finally, data flows between the client and the server. Both the client and the server encrypt data using the new shared key.
In this example, we didn’t use certificate pinning. The client does some basic validation on the cert but doesn’t verify that the cert matches any known cert. It checks to see if the cert’s root came from a trusted certificate authority and that the server in the cert matches the server that the client connects to.
Although HTTPS results in much better security than using HTTP, the “bad guys” trying to listen in on your network traffic could still succeed if the trusted certificate provider gets compromised due to a security vulnerability.
So, what do we do to secure our data transmission even more? We pin specific certs that we trust to our client application.
What Is Certificate Pinning?
Certificate pinning forces your client app to validate the server’s certificate against a known copy. After pinning your server’s certificate inside your client app, your client should check the basic validity of the cert as in No. 3 from the list above, as well as verify that the server’s certificate matches the pinned certificate.
To verify we have a match, the client can validate against the entire cert or against the public key. However, instead of a direct copy of the cert or key, we instead use a fingerprint. A fingerprint is a hashed version of either the entire cert or the public key. If the fingerprints between the server and the client side cert match, the connection is valid. If they don’t match, the app should reject the connection.
Another fun fact—pinning can be preloaded into the application, or it can automatically pin whatever certificate the server sends during the first client-to-server call. Preloading protects the application more protection, as an attacker might be able to pin their own certificate upon the first call.
Do I Pin the Cert or the Public Key?
Typically, security folks advise you to pin the public key. This has a few advantages. First, if you don’t do this and certificates rotate frequently, then you’d need to push out new pinned certs frequently. However, if you pin just the public key, the rotated cert usually keeps the same public key. Either way, you may want to consider pinning a backup cert or public key in case one expires.
Which Cert in the Chain Do I Pin?
If you’ve ever opened up a cert, you may have noticed that it’s not just one cert inside. When creating certificates for your server, you’ll use—at a minimum—a trusted certificate authority to build it off of. However, you can also add more layers into your cert, creating a certificate chain. Let’s take a look at certificate chains and see what pinning them would do.
Leaf Cert: A leaf cert is the top level cert in a certificate chain. Pinning a leaf certs brings us to almost complete certainty that the certificate matches. However, if you cycle your leaf certs often, updates need to roll out fairly frequently to make sure your customer’s app continues to work.
Intermediate Cert: The intermediate cert lives between the leaf and root cert. In this case, pinning against the intermediate cert, you’re putting your trust in the intermediate certificate authority. Therefore, you can update your server’s leaf cert more often, as the validation of certs occurs on the intermediate cert.
Root Cert: Finally, the root cert comes from the trusted certificate authority. Pinning the root cert alone puts trust in the root cert authority, as well as all intermediaries that the root cert authority trusts.
Ultimately, you can choose which certs to pin but it is essential for high-security applications to pin at least one certificate in the chain that is controlled exclusively by your organization. An easy mistake to make would be to pin only the root cert of a public certificate authority (CA). This mistake would allow an attacker who obtains a fraudulent certificate from the same CA and have it accepted by the client as authentic.
How Do I Pin Certs in My Mobile App?
Many methods exist for actually pinning certs into your application.
You’ll have to choose between hard-coding the cert into your application code or putting the cert pin into configuration files that are bundled in your application at build time. Preferably, you’ll go the configuration route, as that keeps your certs out of your source code repository and away from prying eyes.
When considering which option to chose, find one where you can not only add the cert through configuration, but that you can also bypass the cert check when necessary. We’ll go into why that’s important a little later on.
Although many more ways exist for pinning certs in your Android app, the following three are most often used:
- TrustManager: A TrustManager instance decides whether the app should accept the credentials from the server. This involves multiple changes to your application and can easily be implemented incorrectly, leading to bugs.
- OkHttp and CertificatePinner: Using OkHttp for your server calls provides a simple mechanism for implementing certificate pinning. The cert fingerprint should be injected into your application at build time. Then, once you have your CertificatePinner ready, add it to your HTTP client, and you’re ready to go.
- Network Security Configuration (NSC): With NSC, you must add certificate pinning into your configuration using XML files that contain fingerprints.
With iOS, the best option relies on TrustKit, an open-source SSL pinning library. With TrustKit you can implement pinning using simple APIs.
Without TrustKit you may end up implementing from scratch, which can be dangerous. If you don’t know best practices or your design is flawed, you could be susceptible to attacks.
In .NET, we can pin certificates using ServicePointManager. As with Android’s CertificatePinner, we can hard-code the fingerprint into the source code or pull it from configuration. Again, pulling from configuration at build time is preferred.
Why Should We Pin Based on a Build Flag?
In a previous section, I mentioned that we may want to build in cert pinning as an optional flag.
Now, you may be thinking, “Why would I ever not want cert pinning? You made it sound so awesome!”
Well, yes, cert pinning is great. However, it complicates the work of security assessment companies, like Carve, when testing application security.
What Is a Man-in-the-Middle Attack?
We often hear about man-in-the-middle attacks. But how do these work?
In the case of our mobile application, our client will make a call to what it thinks is our server. However, a man in the middle intercepts that message and then relays it to your server. The man in the middle then receives the server’s public key but passes on its own distinct public key to your client. Then the attacker can talk to both your client and server, but your client and server aren’t able to talk to each other.
By doing this, they can listen in to any data passed between your client and server, and they can also modify the data being sent back and forth.
Why Does Carve Want to Bypass Pinning?
So again, why would we avoid pinning a cert in some scenarios? Not pinning a cert gives Carve the ability to assess your application and security further by inspecting and modifying client-server communications.
This visibility and control enables Carve to provide you with more information about security flaws in the application. Therefore, leave cert pinning as a build flag so that Carve can create a build without cert pinning.
In summary, we want to use certificate pinning in our applications to reduce the chance of man-in-the-middle attacks. Furthermore, we want to base it on configuration and a build flag to keep the logic out of our source code and to allow additional security testing to occur.
Author: Sylvia Fronczak