Mobile App Security
Secure mobile applications against reverse engineering, data theft, network interception, and platform-specific vulnerabilities. Covers certificate pinning, secure storage, biometric authentication, code obfuscation, and the OWASP Mobile Top 10.
Mobile apps run on devices you do not control, on networks you do not trust, and in environments where a determined attacker has physical access. The threat model is fundamentally different from server-side security — the client is hostile territory.
OWASP Mobile Top 10
| # | Risk | Mitigation |
|---|---|---|
| M1 | Improper Credential Usage | Secure storage, short-lived tokens |
| M2 | Inadequate Supply Chain Security | Dependency scanning, SBOMs |
| M3 | Insecure Authentication/Authorization | OAuth 2.0, biometrics, server-side validation |
| M4 | Insufficient Input/Output Validation | Validate on client AND server |
| M5 | Insecure Communication | TLS 1.3, certificate pinning |
| M6 | Inadequate Privacy Controls | Data minimization, encryption at rest |
| M7 | Insufficient Binary Protections | Obfuscation, tamper detection |
| M8 | Security Misconfiguration | Remove debug flags, secure exports |
| M9 | Insecure Data Storage | Keychain (iOS), EncryptedSharedPreferences (Android) |
| M10 | Insufficient Cryptography | Platform crypto APIs, no custom crypto |
Secure Storage
iOS Keychain
import Security
func saveToKeychain(key: String, data: Data) -> Bool {
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrAccount as String: key,
kSecValueData as String: data,
kSecAttrAccessible as String: kSecAttrAccessibleWhenUnlockedThisDeviceOnly
]
SecItemDelete(query as CFDictionary) // Remove existing
let status = SecItemAdd(query as CFDictionary, nil)
return status == errSecSuccess
}
Android EncryptedSharedPreferences
val masterKey = MasterKey.Builder(context)
.setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
.build()
val encryptedPrefs = EncryptedSharedPreferences.create(
context,
"secure_prefs",
masterKey,
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
)
encryptedPrefs.edit().putString("auth_token", token).apply()
Certificate Pinning
// OkHttp certificate pinning
val client = OkHttpClient.Builder()
.certificatePinner(
CertificatePinner.Builder()
.add("api.example.com", "sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=")
.add("api.example.com", "sha256/BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB=")
.build()
)
.build()
// URLSession certificate pinning
class PinningDelegate: NSObject, URLSessionDelegate {
func urlSession(_ session: URLSession,
didReceive challenge: URLAuthenticationChallenge,
completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
guard let serverTrust = challenge.protectionSpace.serverTrust,
let certificate = SecTrustGetCertificateAtIndex(serverTrust, 0) else {
completionHandler(.cancelAuthenticationChallenge, nil)
return
}
let serverCertData = SecCertificateCopyData(certificate) as Data
let pinnedCertData = loadLocalCertificate()
if serverCertData == pinnedCertData {
completionHandler(.useCredential, URLCredential(trust: serverTrust))
} else {
completionHandler(.cancelAuthenticationChallenge, nil)
}
}
}
Biometric Authentication
// iOS Face ID / Touch ID
import LocalAuthentication
func authenticateWithBiometrics(completion: @escaping (Bool) -> Void) {
let context = LAContext()
var error: NSError?
guard context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error) else {
completion(false)
return
}
context.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics,
localizedReason: "Authenticate to access your account") { success, error in
DispatchQueue.main.async { completion(success) }
}
}
Anti-Patterns
| Anti-Pattern | Consequence | Fix |
|---|---|---|
| Secrets in code/strings.xml | Extracted by decompilation | Server-side secrets, obfuscation |
| HTTP for any API calls | Network interception | TLS everywhere + certificate pinning |
| Storing tokens in plain SharedPrefs | Extracted from rooted devices | Keychain / EncryptedSharedPreferences |
| No code obfuscation (Android) | Easy reverse engineering | ProGuard / R8 obfuscation |
| Logging sensitive data | Visible in logcat / device console | Strip logs in release builds |
Mobile security is defense in depth. No single measure is sufficient. Layer secure storage, encrypted communication, certificate pinning, biometric authentication, and server-side validation to protect user data on hostile devices.