You've probably heard of HTTPS and how the green padlock in your browser gives us peace of mind. However, in the world of app development, blindly trusting that the certificate is valid isn't always enough. There's a latent risk where, even with an encrypted connection, someone could be secretly listening to what's happening between your device and the server.
To address this security vulnerability, a technique called Certificate Pinning has emerged . Essentially, it tells our application: "Don't trust just any certificate authority; only accept the connection if the server presents this exact certificate or public key." It's like putting a very strict gatekeeper at the door of our communication.
What exactly is SSL Pinning and why do we need it?
To understand this properly, we must first remember that standard HTTPS is based on a chain of trust . When you connect to a server, it sends you its digital certificate. Your mobile device checks that this document has been signed by a Certificate Authority (CA) trusted by the operating system. If the signature is valid and the certificate has not expired, the connection is established.
The problem arises when an attacker manages to infiltrate the connection mid-stream, the infamous Man-in-the-Middle (MITM) attack . If the hacker gets a Certificate Authority (CA) to issue a fraudulent but valid certificate, or if they manage to install a malicious root certificate on the victim's device, the operating system will approve the connection. At that point, the attacker can intercept and modify sensitive data, such as bank details or passwords, without the user noticing anything unusual.
This is where pinning comes in. Instead of trusting any recognized CA, the app stores a copy of the server's certificate or public key hash. During the handshake, the app compares the received certificate with the one it has stored. If they don't match bit by bit, the connection is immediately terminated, preventing server spoofing.
Types of anchoring: Certificates versus Public Keys
Not all pinning is done the same way. Depending on what we decide to "pin," we have two main approaches. The first is Certificate Pinning , where we store the complete certificate file (.pem or .der). It's the simplest option to implement but the most tedious to maintain, since every time the server certificate expires and is renewed, we'll have to update the application in the store, or users will lose service.
The second option is Public Key Pinning . In this case, we only store the hash (usually SHA-256) of the certificate's public key. The major advantage is that the public key can remain intact even if the certificate is renewed, which gives us greater certificate agility and reduces the frequency of forced app updates. Furthermore, by storing only a hash, the code is cleaner and more difficult for someone attempting reverse engineering to extract.
Technical implementation on different platforms
If we are working with AndroidThe most modern way is to use the file network_security_config.xmlIn this XML file we define the domains and hashes of the certificates (pins) that we want to authorize, allowing us to Activate essential security settings on Android natively. Then, we simply link this file in the AndroidManifest.xmlIn the past, more complex things were done by overwriting the class. DefaultHttpClient and managing Java certificate stores through keytoolHowever, the XML-based configuration is much more efficient.
In the ecosystem of iOSThings change a bit. We can take advantage of App Transport Security (ATS) by configuring the file Info.plist to define the anchored domains. However, for complete control, it is common to implement runtime validation using URLSessionDelegateIn the authentication challenge method, we extract the certificate from the server and compare it with the local stored certificate in the application bundle.
For those who use Capacitor or hybrid frameworksThere are plugins like @capgo/capacitor-ssl-pinning that unify the process. In these cases, the configuration is usually done in the app's configuration file, specifying the certificate list and enabling validation. It is vital to test these implementations with tools such as Charles Proxy, simulating a MITM attack to confirm that the application effectively rejects the connection when the certificate is not the expected one.
Risks, limitations and maintenance
Pinning isn't all sunshine and roses. The biggest danger is what's called expiration lockout . If the server certificate expires and you haven't updated the app with the new PIN, all your users will see connection errors. To avoid this disaster, the best practice is to implement backup PINs . We store the hash of a secondary public key that we'll only use in case of emergency or unexpected rotation.
On the other hand, pinning can be a headache for developers who need to debug their communications. For monitoring tools to work, it's sometimes necessary to disable pinning in development environments or create specific debug versions, always ensuring that this functionality is enabled in the production version that reaches the end user.
Regarding the web, Chrome was a pioneer in implementing similar concepts. It uses HSTS (HTTP Strict Transport Security) to enforce the use of HTTPS and maintains a list of "preloaded" sites to prevent first-contact vulnerability. Although the HPKP standard did exist for the web, it eventually fell into disuse because it was too risky and could render websites inaccessible if a configuration error occurred.
Implementing certificate pinning is a strategic decision that balances maximum security with service operability. By restricting trust to specific entities, we protect communication against sophisticated attacks, provided we maintain rigorous control over the lifecycle of our keys and certificates to avoid leaving customers without access.
