Mobile applications handle sensitive user data—credentials, financial information, personal details—in an environment where the device itself may be compromised. Unlike web applications protected by server-side infrastructure, mobile apps execute on hardware the developer does not control. This reality demands a defense-in-depth approach to security encompassing authentication, data encryption, secure storage, network security, and code protection.
Biometric Authentication
Biometric authentication (fingerprint, face recognition) provides a strong balance between security and usability. On iOS, the LocalAuthentication framework provides Face ID and Touch ID through a unified API. On Android, the BiometricPrompt API standardizes fingerprint, face, and iris authentication across manufacturers. Both platforms handle the biometric data entirely within secure hardware enclaves—your application never sees the raw biometric data.
Implement biometric auth as a secondary authentication factor or as a convenience unlock after initial credential-based authentication. Store an encryption key (not the password itself) in the platform's secure enclave, gated behind biometric verification. When the user authenticates biometrically, the key is released to decrypt a locally stored authentication token. This approach means a compromised device backup does not expose credentials.
Handle biometric unavailability gracefully. Devices may lack biometric hardware, users may not have enrolled biometrics, or biometric data may change (new fingerprint enrolled). Always provide a fallback authentication path—typically PIN or password—and re-authenticate with credentials when biometric enrollment changes are detected.
Secure Storage: Keychain and Keystore
Never store sensitive data in plain text—not in AsyncStorage, SharedPreferences, UserDefaults, or local files. iOS provides the Keychain Services API for storing small pieces of sensitive data (tokens, passwords, encryption keys) in a hardware-backed encrypted container. Android's equivalent is the Keystore system, which stores cryptographic keys in a hardware-backed keystore and provides the EncryptedSharedPreferences API for key-value data.
In React Native, libraries like react-native-keychain and react-native-sensitive-info abstract platform-specific secure storage behind a unified API. In Flutter, the flutter_secure_storage package provides similar cross-platform abstraction. Configure the appropriate access control level—for example, requiring biometric authentication to access specific keychain items.
Access control matters: set keychain items to be accessible only while the device is unlocked (kSecAttrAccessibleWhenUnlocked on iOS) and exclude them from backups to prevent extraction through cloud backup restoration. On Android, use setUserAuthenticationRequired to bind key access to device authentication.
Data Encryption
Encrypt sensitive data at rest using AES-256 in GCM mode for authenticated encryption. Generate encryption keys using the platform's secure random number generator and store them in the Keychain or Keystore—never hardcode keys in source code or derive them from predictable inputs. For database encryption, SQLCipher provides transparent AES-256 encryption for SQLite databases on both platforms.
Data in transit must use TLS 1.2 or higher. Configure your server to disable older TLS versions and weak cipher suites. On the client, enforce App Transport Security (ATS) on iOS and Network Security Configuration on Android to prevent accidental cleartext communication. These platform mechanisms block HTTP requests and connections to servers with invalid certificates by default.
Certificate Pinning
Certificate pinning prevents man-in-the-middle attacks by validating that the server's certificate matches a known, expected certificate or public key. Without pinning, a compromised Certificate Authority or a device with a malicious root certificate installed could intercept encrypted traffic transparently.
Pin the public key (Subject Public Key Info) rather than the entire certificate to avoid pin rotation issues when certificates are renewed. Maintain a backup pin for your next certificate's public key. Implement a pin failure reporting mechanism to detect potential attacks and accidentally broken pins. Libraries like TrustKit (iOS) and OkHttp's CertificatePinner (Android) provide straightforward pinning implementations.
Be cautious with pinning in development—it can block debugging proxies like Charles Proxy. Use build configurations to disable pinning in debug builds while enforcing it in release builds.
Code Obfuscation and Tamper Detection
Mobile app binaries are distributed to end users, making reverse engineering a real threat. On Android, ProGuard (and its successor R8) obfuscates class names, method names, and removes unused code. Enable R8 in release builds with appropriate keep rules for reflection-dependent code, serialization models, and native method signatures.
iOS apps compiled with Swift benefit from some inherent obfuscation, but consider additional measures for high-security applications. Runtime integrity checks can detect whether the app is running in a debugger, on a jailbroken or rooted device, or has been tampered with. Respond to detected tampering contextually—a banking app should refuse to operate on a rooted device, while a content app might simply disable certain features.
Security-First Development
Security is not a feature to add at the end—it is an architectural decision that influences every layer of the application. At Nexis Limited, our mobile development services incorporate security best practices from the initial design phase through deployment and ongoing maintenance. Contact us for a security review of your existing mobile application or to discuss secure architecture for a new project.