What Is SSL Pinning in Android Apps? How It Works and Where It Falls Short
SSL pinning (more accurately certificate or public-key pinning) is a technique where an app accepts only specific, pre-chosen server certificates instead of trusting every certificate authority on the phone. It shrinks the chance that someone can silently intercept an app's encrypted traffic.
- Pinning makes an app accept only specific certificates or public keys instead of every certificate authority the phone trusts.
- It defends against rogue or compromised CAs and intercepting proxies, and on Android is declared in a network security config
pin-set.[1] - Always ship a backup pin and plan rotation; a wrong pin locks users out of the app.
- Pinning is not a guarantee: on a device the attacker controls it can often be bypassed, so keep authorization on the server.
SSL, TLS and the "pinning" idea
Apps protect network traffic with TLS. SSL is the older protocol it replaced, but the name stuck, so "SSL pinning" is the common phrase.
Normally, when an app connects to api.example.com, TLS checks that the server's certificate chains up to a certificate authority (CA) the phone already trusts. The weak point: that trust list is long, and if any trusted CA is compromised or misused — or a user is tricked into installing a rogue CA — an attacker can present a valid-looking certificate and sit in the middle.
Pinning narrows the trust. The app ships with the expected certificate, or a hash of its public key, and rejects connections that do not match, even when the certificate would otherwise validate.
What pinning protects against
- A compromised or careless certificate authority issuing a certificate for your domain
- User-installed CA certificates, such as those added by a malicious profile or an intercepting proxy
- Hostile networks that try to swap certificates
Android's documentation notes that only apps targeting Android 6.0 (API level 23) or lower trust the user-added CA store by default[1], so newer apps already close part of this gap. Likewise, cleartext (unencrypted) traffic has been blocked by default since Android 9 (API level 28).[1] Pinning goes further by also limiting which system CAs the app will accept for a given server.
How pinning is implemented on Android
There are three common ways, in order of preference.
1. Network Security Configuration (declarative)
The manifest points to an XML file with a pin-set for each domain. This is the platform-supported route described in the Android documentation:
<network-security-config>
<domain-config>
<domain includeSubdomains="true">api.example.com</domain>
<pin-set expiration="2027-01-01">
<pin digest="SHA-256">AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=</pin>
<pin digest="SHA-256">BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB=</pin>
</pin-set>
</domain-config>
</network-security-config>
The manifest references it with android:networkSecurityConfig="@xml/network_security_config". App Checker's analyzer can read this declaration.
2. In code with an HTTP library
Libraries such as OkHttp expose a CertificatePinner that checks the server's public-key hash during the handshake. This is flexible but invisible to anyone looking only at the manifest.
3. A custom TrustManager
Writing your own certificate validation is error-prone. A mistake that accepts all certificates silently removes TLS protection, which is why Android's guidance on unsafe TrustManager implementations warns against it.[2]
The catch: certificate rotation can lock users out
Pin the wrong thing, or forget to update it, and every user's app stops connecting the moment the server certificate changes. Practical safeguards:
- Pin public keys, not leaf certificates. The key can stay the same across renewals.
- Always include a backup pin for a key you control but do not yet use, so you can rotate without shipping an emergency update. Android's own example includes one.[1]
- Weigh an expiration carefully. A pin-set expiration prevents lock-outs in apps that were never updated, but Android's documentation warns it may let attackers bypass your pinned certificates once it passes.[1]
- Test the rotation path before you need it.
What pinning cannot do
Pinning defends traffic that leaves the device. It does not protect an app on a phone the attacker fully controls.
A security tester or attacker with a rooted device or an emulator can use instrumentation tools to change an app's behaviour at runtime, including the code that checks pins. That is a limit of any client-side check: it raises the cost of interception without making it impossible. Treat pinning as one layer, alongside server-side authorization and short-lived credentials, as OWASP's MASVS-NETWORK control group frames it.[3]
Pinning also blocks legitimate inspection proxies. If you test your own app, you will need a debug build or configuration that relaxes pinning for development only.
How to check whether an app uses pinning
For manifest-declared pinning, upload the APK to the APK analyzer: it resolves the networkSecurityConfig reference and lists any pin-set domains and digests. Pinning done purely in code (for example with OkHttp) is not visible at this level and needs decompilation to confirm, and OWASP's testing guide describes how pinning is tested in practice.[4] See our guide on how to analyze an APK.
Frequently asked questions
Is SSL pinning the same as certificate pinning?
Yes in everyday use. Strictly, apps use TLS rather than SSL, and they can pin either the full certificate or just its public key. "SSL pinning" is the popular shorthand for both.
Does SSL pinning make an app unhackable?
No. It makes network interception harder, but an attacker who controls the device can often alter the app at runtime. It works best combined with server-side checks.
What happens when a pinned certificate expires?
If the app pinned only that certificate, connections fail until users update. Pinning public keys, adding backup pins and setting a pin-set expiration prevent this.
Do all apps use SSL pinning?
No. It is more common in banking, payments and security-sensitive apps. Many apps rely on standard TLS validation, which is a reasonable default for lower-risk data.
What is the difference between certificate pinning and public key pinning?
Certificate pinning trusts one exact certificate, so it breaks whenever the certificate is renewed. Public key pinning trusts the key inside it, which can stay the same across renewals, and is the safer, more common choice.
How do I add SSL pinning to an Android app?
The platform-supported way is a network security config with a pin-set per domain, referenced from the manifest. You can also use an HTTP library's pinning feature, such as OkHttp's CertificatePinner.
Can SSL pinning be bypassed?
On a device the tester or attacker controls, such as a rooted phone or emulator, runtime tools can often alter the check. Pinning raises the cost of interception but does not make it impossible.
Why can't I inspect an app's traffic with a proxy?
If the app pins its certificates, it rejects the proxy's certificate even when you have installed the proxy's CA on the phone. That is the feature working as intended.
Does pinning interfere with DNS-based content filters?
No. Filters that work at the DNS level only look up domain names; they do not decrypt traffic, so pinning does not get in their way.
- Network security configuration — Android Developers — user-added CA trust by API level, cleartext defaults, pin-set, backup pin and expiration trade-off.
- Unsafe implementation of the TrustManager interface — Android Developers — why custom certificate validation is risky.
- OWASP MASVS — MASVS-NETWORK — network communication requirements for mobile apps.
- OWASP Mobile Application Security Testing Guide (MASTG) — how to test network security and pinning.