Mobile Application Security: OWASP MASVS, Pinning and Reverse Engineering Protection
OWASP Mobile Application Security Verification Standard is not an academic document. It's a checklist of what a penetration tester examines in your application. What they find is often uncomfortable.
Certificate Pinning: Why and How Not to Break Production
Certificate Pinning is the binding of an application to a specific TLS certificate or its public key. Without it, application traffic is intercepted through a proxy (Charles, mitmproxy) in five minutes — this is OWASP MASVS-NETWORK-2.
On iOS it is implemented via URLSessionDelegate.urlSession(_:didReceive:completionHandler:) with SecTrust verification. Or through TrustKit — a library with declarative configuration via Info.plist. TrustKit can also send reports of failed checks to the server — useful for monitoring MITM attacks in production.
On Android — network_security_config.xml:
<network-security-config>
<domain-config>
<domain includeSubdomains="true">api.example.com</domain>
<pin-set expiration="2026-01-01">
<pin digest="SHA-256">base64_public_key_hash</pin>
<pin digest="SHA-256">backup_key_hash</pin>
</pin-set>
</domain-config>
</network-security-config>
Critical: always two pins — primary and backup. If the certificate expires and backup pin is not configured, all users cannot log into the application until the next update. This is exactly how large applications went down.
Another failure point: CDN and third-party SDKs. If advertising SDK or analytics make requests to their own servers, and global pinning is configured in network_security_config — the SDK breaks. Configuration must be subdomain-specific.
Code Obfuscation and Protection
iOS: Swift code compiles to native binary that does not decompile to readable Swift. But Objective-C runtime and Mach-O metadata provide much information through class-dump and nm. Class names, method names, strings in the binary — all visible. For critical strings (configuration keys, not API keys — they should not be there) — obfuscation via SwiftShield.
Android: Java/Kotlin compiles to DEX, which reads through jadx in seconds. R8 (enabled by default in release builds) minifies and obfuscates. But ProGuard/R8 rules require careful configuration: often after enabling obfuscation, the application crashes in production due to reflection or Gson serialization. Debug -dontwarn rules accumulated over years — source of security holes.
For maximum Android protection — DexGuard (paid) or free DexProtector. They add runtime protection, string encryption and integrity checks.
Jailbreak and Root Detection
MASVS-RESILIENCE-1 requires detection of compromised devices. Standard checks: presence of /Applications/Cydia.app, /usr/bin/ssh, ability to write a file outside sandbox (/private/jailbreak_test), presence of MobileSubstrate.
But: static checks are easily bypassed through A-Bypass, Liberty Lite and similar tweaks. Serious protection is built on multiple layers with runtime checks that are not trivially intercepted via frida or fishhook.
Ready-made solutions: IOSSecuritySuite (iOS, open source), rootbeer (Android). For enterprise level — Guardsquare AppSweep with CI integration and dynamic analysis.
Data Storage and Keychain
MASVS-STORAGE-1 and STORAGE-2 are the most frequently violated requirements.
Common iOS mistake: authorization tokens stored in UserDefaults. Data from UserDefaults is backed up to iCloud and available when restoring to another device. Token from UserDefaults on a new device — this is someone else's authorized session. Correct approach: Keychain with kSecAttrAccessibleWhenUnlockedThisDeviceOnly and kSecAttrSynchronizable = false.
On Android similarly: SharedPreferences stored in open XML on devices without encryption (/data/data/). EncryptedSharedPreferences from Jetpack Security or Android Keystore for critical data.
| Vulnerability | MASVS Requirement | Verification Tool |
|---|---|---|
| Unprotected traffic | NETWORK-1,2 | mitmproxy, Burp Suite |
| Data in UserDefaults/SharedPrefs | STORAGE-1 | frida, objection |
| No pinning | NETWORK-2 | Charles Proxy |
| Weak obfuscation | RESILIENCE-3 | jadx, class-dump |
| No jailbreak detection | RESILIENCE-1 | Jailbroken device test |
Timeline: security audit per OWASP MASVS level L1 — 1-2 weeks. Implementing a protective layer for an existing application — 3-6 weeks depending on issues found.







