Certificate Pinning Explained

When your phone's banking app connects to api.bank.com, it uses TLS to encrypt the connection. But TLS alone only guarantees that the connection is encrypted — it doesn't guarantee that you're talking to the real api.bank.com. A man-in-the-middle attacker with the ability to intercept traffic can present their own certificate, and a standard TLS implementation will accept it if it's signed by a trusted Certificate Authority (CA).

Certificate pinning solves this by embedding the expected server certificate or public key directly in the application. When the app connects, it checks the server's certificate against the pinned value. If they don't match, the connection fails — even if the certificate is technically valid and signed by a trusted CA.

Why Mobile Apps Often Fail Certificate Pinning

Despite being a well-understood security measure, certificate pinning failures in mobile apps are rampant:

  • Custom SSL validation code that can be bypassed: Many developers implement pinning incorrectly — they add a onSSLError handler that calls on exception, which is the first thing proxy tools like Burp Suite look for.
  • Pinning the wrong thing: Some apps pin the intermediate CA instead of the leaf certificate. When the CA rotates its intermediate certificate (which happens regularly), the app breaks and developers disable pinning entirely.
  • No pinning for older OS versions: Some apps only enforce pinning on iOS 13+ or Android 10+, leaving older devices vulnerable.
  • Frida/objection can bypass pinning at runtime: Even strong pinning can be bypassed without touching the APK — a Frida script injected at runtime can hook the SSL validation function and return true regardless of the certificate.
# Checking if an app uses certificate pinning (Android)

$ jadx -d output app-release.apk

$ grep -r "okhttp" output/sources/ | grep -i "CertificatePinner\|Certificate"

[Found OkHttp with CertificatePinner in use — checking configuration...]



# Using objection to test pinning at runtime

$ objection --gadget com.target.app run android sslpinning disable

[objection] SSL Pinning disabled for: com.target.app

[Now traffic that was previously pinned flows through Burp Suite transparently]

[This demonstrates why pinning alone is not enough — network-level controls are still needed]