What is SSL Pinning
SSL Pinning, also known as certificate pinning or public key pinning, is a security mechanism used in mobile and desktop applications to prevent man-in-the-middle (MITM) attacks. When an app communicates with a server, it normally trusts any certificate that is signed by a recognized Certificate Authority (CA). SSL Pinning takes this a step further by hardcoding the server's expected certificate or public key directly inside the application itself.
This means even if an attacker manages to install a trusted CA certificate on a device, the application will reject any TLS connection that does not match its pinned certificate. Because of this, SSL Pinning is widely adopted in banking apps, payment platforms, and any application that handles sensitive user data.
However, SSL Pinning is not unbreakable. Researchers and attackers have developed several reliable techniques to bypass it, especially on mobile platforms like Android and iOS. Understanding how these bypasses work is critical for security engineers who want to implement pinning correctly and defend against circumvention.
Key Concepts in SSL/TLS
Certificate Authority (CA): A trusted third party that signs and validates SSL certificates.
TLS Handshake: The process where client and server negotiate encryption before exchanging data.
Certificate Pinning: Validating the server certificate against a hardcoded expected value in the app.
Public Key Pinning: A variant of pinning that checks only the public key portion of the certificate, surviving certificate renewals.
How SSL Pinning Works
In a standard TLS connection, the client (your app) receives the server's certificate and checks whether it was signed by a CA in the device's trusted store. If the check passes, the connection proceeds. This model has a well-known weakness: anyone who can add a trusted CA to the device (like an attacker running a proxy) can issue a fraudulent certificate for any domain and intercept the traffic.
SSL Pinning solves this by adding a second validation step. After receiving the server certificate, the app compares it against a value stored at build time. There are two common approaches:
Certificate Pinning: The app stores the full certificate (or its SHA-256 hash). If the server presents a different certificate, the connection is rejected, even if it is signed by a valid CA.
Public Key Pinning: The app stores only the public key from the certificate. This is more flexible because the certificate can be renewed with a new validity period while keeping the same public key.
Pinning is typically implemented in mobile apps using platform libraries or third-party networking libraries. On Android, common implementations use OkHttp's CertificatePinner, TrustManager overrides, or the Network Security Config XML file. On iOS, developers use URLSession delegate methods or libraries like TrustKit.
The Problem During Testing
When performing a mobile application penetration test, one of the first steps is to intercept the application's network traffic using a proxy tool such as Burp Suite or OWASP ZAP. The standard workflow involves installing the proxy's CA certificate on the test device, routing traffic through the proxy, and then analyzing requests and responses.
This approach works perfectly for applications that rely only on the system CA store. However, when SSL Pinning is in place, the application detects that the certificate presented by the proxy does not match its pinned value and immediately terminates the connection. In practice, this looks like the app returning network errors or simply refusing to load any data, while Burp Suite shows no intercepted traffic at all.
At first glance, it appears the application is completely secured from traffic interception. The question then becomes: is this pinning implemented correctly and consistently across all API calls, or are there gaps that can be exploited?
Bypass Techniques
Over the years, security researchers have documented multiple reliable techniques to bypass SSL Pinning on both Android and iOS. Each method targets a different layer of the implementation, from the OS level to the app's own code.
Method 1: Frida Dynamic Instrumentation
Frida is a dynamic instrumentation toolkit that lets you inject JavaScript into running processes on Android and iOS. It is one of the most powerful and widely used tools for SSL Pinning bypass during penetration testing.
The idea is simple: hook the functions responsible for certificate validation inside the running app process and replace them with code that always returns success, regardless of the certificate presented. Pre-written Frida scripts exist specifically for this purpose, such as the popular ssl-pinning-bypass script by shroudedcode.
On Android with a rooted device or using Frida server, the command to inject a bypass script is:
frida -U -f com.target.app -l ssl_pinning_bypass.js --no-pause
This approach works against most common pinning implementations including OkHttp CertificatePinner, Conscrypt, HttpsURLConnection, and even some custom TrustManagers. However, it requires either a rooted Android device or a jailbroken iOS device.
Method 2: Objection Framework
Objection is built on top of Frida and provides a higher-level interface for mobile app security testing. It includes an automated SSL Pinning bypass feature that works with a single command, without needing to write custom Frida scripts.
After connecting Objection to the target app, SSL Pinning can be disabled with:
android sslpinning disable
Objection automatically hooks multiple pinning-related methods across different libraries in one go. This makes it ideal for quickly assessing whether an app's pinning can be bypassed during an engagement, before investing time in deeper analysis.
Method 3: Network Security Config Manipulation (Android)
Android 7.0 and above introduced a declarative security configuration through the Network Security Config XML file. This file, referenced in the AndroidManifest, controls which CAs the app trusts, whether cleartext traffic is allowed, and whether certificate pinning is enforced.
If the APK can be decompiled, the network_security_config.xml file can be modified to remove pinning declarations or add the tester's CA as a trusted anchor, then the APK can be recompiled and signed. The relevant section to remove or neutralize looks like:
<pin-set expiration="2026-01-01">
<pin digest="SHA-256">base64EncodedHash==</pin>
</pin-set>
After patching the config, the APK is rebuilt using apktool and signed with a test keystore using apksigner. This method is useful when Frida is not available, but it requires that the app does not perform additional integrity checks (such as root detection or APK signature validation).
Method 4: Magisk + TrustUserCerts Module (Android)
On rooted Android devices using Magisk, the MagiskTrustUserCerts module automatically moves user-installed CA certificates into the system CA store. Since most apps trust the system CA store without explicit pinning, this allows a proxy certificate to be trusted at the OS level.
This method does not bypass pinning directly in the app code, but it eliminates the need for a bypass by making the proxy's certificate appear legitimate at the system level. It is particularly effective against apps that implement pinning loosely or only at the network library level without additional checks.
Method 5: SSL Kill Switch 2 (iOS)
On jailbroken iOS devices, SSL Kill Switch 2 is a tweak that patches the SecureTransport and NSURLSession APIs at a system level to disable certificate validation globally. Once installed via Cydia or Sileo, it effectively disables SSL Pinning for all apps on the device.
This is the iOS equivalent of a system-wide Frida bypass and works against apps that do not implement additional custom validation beyond the standard Apple APIs. Apps using third-party TLS libraries or implementing pinning in native code may require additional effort.
Method 6: Reversing and Patching the Binary
For apps with advanced protections that prevent dynamic instrumentation, a static approach can be used. By disassembling the application binary using tools like Ghidra, jadx (for Android), or Hopper (for iOS), the certificate validation logic can be located and patched to always return success.
On Android, this typically means finding the checkServerTrusted method in a custom TrustManager class and replacing its implementation with a no-op (empty method that does not throw exceptions). On iOS, the relevant function is often a custom block or delegate method that compares certificates.
This method is more time-consuming but is effective even against apps with anti-Frida or anti-tamper detection, because it does not require runtime injection.
What Happens in Practice
During a mobile application penetration test, the initial attempt to proxy traffic through Burp Suite resulted in complete connection failure. The application displayed a generic network error and no requests appeared in the proxy. This immediately indicated SSL Pinning was in place.
The first bypass attempt was made using Objection on a rooted Android device. After attaching to the application process and running the sslpinning disable command, the app began successfully loading data. Traffic was now fully visible in Burp Suite.
However, not all endpoints were immediately accessible. Some API calls were still failing. Further investigation using jadx to decompile the APK revealed that the application had two separate networking layers: one using OkHttp (successfully bypassed by Objection) and one using a custom implementation with its own TrustManager that performed an additional certificate hash comparison directly in native code.
This secondary pinning required a targeted Frida script to hook the specific native method. Once both layers were bypassed, the full API traffic was visible, which led to the discovery of several additional vulnerabilities including insecure direct object references and an unauthenticated admin endpoint that was never accessible from the UI.
This highlights a critical point: partial pinning implementations create a false sense of security. If only some requests are pinned and others are not, an attacker only needs to find the unprotected paths.
How to Fix SSL Pinning Implementation
Implementing SSL Pinning correctly is more nuanced than simply adding a certificate hash to the code. A bypass-resistant pinning strategy requires attention to multiple layers of the application.
Pin the Public Key, Not the Full Certificate
Pinning the full certificate means the pin becomes invalid every time the certificate is renewed (typically every 1-2 years). Public key pinning is more resilient because the public key can remain stable across renewals. Always pin at least two keys: the current one and a backup, to allow key rotation without a forced app update.
Apply Pinning to All Network Calls
A common mistake is pinning only the primary API client while leaving secondary clients, analytics SDKs, or third-party libraries unpinned. Every outbound HTTPS connection in the app should go through a pinned channel, or the unpinned paths become viable interception targets.
Implement Pinning at the Native Level
Pinning implemented purely in Java or Swift/Objective-C is easier to hook and bypass with Frida. Moving the certificate validation logic into a compiled C/C++ native library (via JNI on Android or a static library on iOS) significantly raises the bar for dynamic instrumentation attacks.
Combine Pinning with Integrity Checks
SSL Pinning alone cannot stop an attacker with a rooted or jailbroken device. Combine pinning with root/jailbreak detection, APK signature verification, and runtime integrity checks. While none of these are individually unbreakable, layering them together increases the effort required significantly.
Use a Key Backup and Rotation Plan
Plan for key rotation from the beginning. If the server's private key is compromised and you have no backup pin deployed in the app, you may be forced to push an emergency app update. A rotation-ready architecture includes:
A primary pinned public key (currently active)
One or more backup pins for planned rotation
A defined process for updating pins via app update or remote configuration with its own integrity guarantee
RASP: Runtime Application Self-Protection
One of the most powerful and often overlooked solutions when it comes to protecting mobile applications against SSL Pinning bypass and similar runtime attacks is RASP, short for Runtime Application Self-Protection. Unlike traditional security controls that live at the network or server level, RASP is embedded directly inside the application itself. It monitors and protects the app from within, at the exact moment code is being executed.
What is RASP?
RASP is a security technology that integrates into an application's runtime environment to continuously observe its behavior. Instead of relying solely on static defenses like SSL Pinning code or compile-time checks, RASP instruments the application at a deeper level, watching for signs of tampering, instrumentation, or unexpected execution patterns as they happen in real time.
Think of it this way: SSL Pinning is a lock on the door. RASP is a security guard standing inside the room who watches what happens even after the door is opened. If something looks wrong, the guard can respond immediately, whether that means blocking the action, terminating the session, or alerting the security team.
Why RASP Matters for SSL Pinning
Every SSL Pinning bypass technique covered in this blog relies on the attacker doing something abnormal at runtime: injecting a Frida gadget, hooking a function, patching memory, or running the app in an instrumented environment. These are exactly the kinds of behaviors RASP is designed to detect and respond to.
A RASP-enabled application can detect and react to:
Frida or Xposed framework presence in the process memory
Dynamic method hooking or unexpected function pointer changes
Debugger attachment to the application process
Execution on a rooted or jailbroken device
APK or IPA signature mismatch indicating repackaging
Changes to the app's own code or security-critical functions at runtime
When any of these conditions are detected, the RASP layer can respond in several ways: silently logging the event for security monitoring, gracefully degrading functionality, blocking the specific action being attempted, or terminating the app session entirely. The response can be tuned based on risk level and use case.
RASP vs Traditional Defenses
Traditional defenses like SSL Pinning, root detection, and obfuscation are all static or build-time measures. They can be studied, reversed, and circumvented with enough effort, because they do not change once the app is shipped. RASP changes the equation by making the app an active participant in its own defense. It does not just hold a position, it watches for the attack and responds.
This is especially important because mobile app attacks almost always involve runtime manipulation. An attacker is not breaking SSL Pinning by reading the code; they are breaking it by hooking functions while the app is running. RASP directly addresses this threat model in a way that static defenses cannot.
Popular RASP Solutions for Mobile
Guardsquare DexGuard / iXGuard: Provides obfuscation combined with RASP capabilities for Android and iOS, including root detection, anti-tampering, and Frida detection.
Appdome: A no-code mobile security platform that can inject RASP, certificate pinning, and anti-fraud controls into existing APKs and IPAs without source code changes.
Promon SHIELD: A dedicated mobile RASP solution widely used in banking and fintech apps, providing comprehensive runtime protection and threat telemetry.
Custom Native RASP: For teams with the resources, embedding custom integrity checks and instrumentation detection directly in C/C++ native libraries provides the strongest possible protection because it is hardest to locate and hook.
It is worth noting that RASP is not a silver bullet either. A sophisticated attacker with enough time can study a RASP implementation and find ways around it. The real value is in the cost it imposes: every bypass attempt requires significant effort, and most attackers will not invest that effort for a single target. Combined with SSL Pinning, obfuscation, and server-side monitoring, RASP forms the backbone of a genuinely resilient mobile security architecture.
How to Protect Your Application
Beyond correct pinning implementation, a robust defense requires thinking about the problem from the attacker's perspective. The goal is not to make bypass impossible, but to make it expensive enough that attackers move on to easier targets or that you detect the attempt.
Use Certificate Transparency (CT) Logs: Monitor CT logs for any unexpected certificates issued for your domain. If an attacker uses a rogue CA, they often need to register the certificate, which appears in CT logs.
Enable Network Security Config on Android: Even if you implement custom pinning, also configure the Network Security Config XML as a defense-in-depth measure. It provides OS-level enforcement that attackers must patch out separately.
Detect Proxy and VPN Usage: Check at runtime if a proxy is configured on the device. While users may legitimately use VPNs, an unexpected proxy setting on a mobile device during an API session can be flagged and logged server-side for review.
Log and Alert on Pinning Failures Server-Side: If pinning is enforced server-side as well (via HPKP headers in web contexts, or equivalent mechanisms), log every pinning failure. Repeated failures from the same account or IP can indicate active interception attempts.
Avoid Storing Sensitive Data on the Device: Even if traffic is intercepted, limiting what the app stores locally reduces what can be extracted. Tokens should have short lifetimes, and sensitive operations should require fresh authentication.
Obfuscate the Pinning Logic: Use code obfuscation tools like ProGuard or R8 on Android and equivalent tools on iOS to make it harder to identify and hook the pinning functions in a static or dynamic analysis.
The most important mindset shift is to treat SSL Pinning as one layer in a defense-in-depth strategy, not as a complete solution. No single control stops a determined attacker with physical access to the device. The real goal is to make interception detectable, logged, and expensive enough to be impractical at scale.
Important Recommendations
To summarize, here are the most critical actions to take when implementing or auditing SSL Pinning in a mobile application:
Always pin the public key rather than the full certificate to survive certificate renewals.
Pin all network endpoints in the app, not just the primary API client, to eliminate unprotected paths.
Move pinning validation into native code to raise the difficulty of dynamic instrumentation bypasses.
Layer pinning with root/jailbreak detection and APK integrity checks for a defense-in-depth posture.
Maintain at least one backup pin and plan your key rotation process before deployment, not after an incident.
Test your own pinning implementation regularly using Frida, Objection, and manual binary analysis during security reviews.
Monitor Certificate Transparency logs and server-side pinning failure logs to detect active interception attempts.
SSL Pinning, when implemented correctly and as part of a layered security model, remains one of the most effective controls against mobile traffic interception. When implemented poorly or in isolation, it provides only the illusion of security.



