Pinning That Fails Open on Android 14
A fintech Android app (minSdk 24, targetSdk 34, version 4.2.0) ships a network_security_config.xml that sets cleartextTrafficPermitted='false' for the base config and adds a domain-config for api.example-bank.com. That domain-config contains a pin-set with expiration='2025-06-30' and two pins: the current leaf certificate SPKI and one backup intermediate CA SPKI. OkHttp also has a CertificatePinner with only the leaf pin. On an unrooted Android 13 device with a user-installed proxy CA, API calls fail with a certificate chain error. On a rooted test device, an engineer runs a Frida script that disables the OkHttp pinner; API calls then succeed through the proxy, and the NSC pin is not observed to block the handshake. The certificate renews every 90 days using the same intermediate CA, and the team has no remote pin configuration. Release is in three weeks.
1. The unrooted device fails closed, yet the rooted device succeeds after only the OkHttp pinner is disabled. What does this tell you about which layer is enforcing pinning, and why is the rooted result not evidence that the production app is safe?
Read the worked answer
The unrooted failure is best explained by trust-anchor selection, not by pinning alone. Android 7+ apps do not trust user-installed CAs unless the app opts in, so the proxy CA is rejected before any pin comparison. The rooted run is different: Frida can hook the app's TrustManager and the OkHttp CertificatePinner, so client-side checks are removed. That proves the controls are bypassable on a device the attacker controls; it does not prove the server is safe, because pinning is a client-side MITM defense, not a server authentication mechanism. It also raises a finding: if the NSC pin-set were effective, disabling only the OkHttp pinner should still leave a platform pin failure. The absence of an NSC failure suggests the NSC pin is not being applied to this connection (custom trust manager, host mismatch, or the hook also replaced the platform trust manager). Verify which layer is active with a clean build, a system CA, and no Frida.
2. What happens on 2025-07-01 if the app has not been updated, and why is that dangerous?
Read the worked answer
On 2025-07-01 the Android NSC pin-set expires. For network_security_config pinning, expiration does not make the app fail closed; it disables pin enforcement for that domain. After that date the app will accept any certificate chain that chains to a trusted CA on the device. Because the OkHttp pinner also only has the leaf pin, the leaf pin may still fail closed until the certificate renews, but the design is inconsistent: one layer fails open, one fails closed, and the leaf pin will break on the 90-day renewal. The real risk is a silent downgrade of protection after expiration, exactly when an attacker with a user CA or a compromised CA can intercept. A pin-set should not be used as a delayed fail-open switch.
3. Design a safer pin set and rotation process given 90-day certificate renewal and no remote configuration.
Read the worked answer
Pin to the intermediate CA SPKI plus a backup key from a different CA/key hierarchy, not the leaf. Include at least two pins so one can be rotated. Set expiration beyond the planned app-update window and monitor it. Use remote config or a feature flag to update pins, but never let remote config disable pinning entirely. Because certificates renew every 90 days, a leaf pin forces a release every quarter. Add certificate transparency monitoring, keep the backup private key offline, and rehearse a renewal in staging. If the app must fail closed, do not rely on NSC expiration; implement a pinner that checks the chain and fails closed, with an emergency update path.