Mobile App

18 practices · OWASP MASVS — Storage, Crypto, Auth, Network, Platform, Code, Resilience, Privacy

React Native and native builds: what goes in the binary, where tokens live, how traffic is protected, and what a release build must not still contain.

Ticking these off for a specific project? The Project Security Checklist tracks all nine categories in one place. For why these patterns keep recurring, see Security Best Practices.

1. No Hardcoded Secrets in the Binary

Description

API keys, signing credentials, and encryption keys never live in source code that ships inside the app binary — anyone can decompile an APK or IPA and read every string in it.

Example
// Never: const API_KEY = 'sk_live_abc123'
// Instead: fetch a short-lived token from your backend
// at runtime, scoped and expiring

2. Platform Secure Storage

Description

Store tokens and sensitive data in iOS Keychain or Android Keystore, which are hardware-backed and OS-protected — never a static encryption key or a shared MMKV/SharedPreferences secret baked into the app.

Example
iOS: kSecClassGenericPassword + kSecAttrAccessible
Android: EncryptedSharedPreferences with a MasterKey

3. Exclude Sensitive Data From Backups

Description

Device backups (iCloud, Android auto-backup) can leave sensitive local files on a third party's servers — explicitly exclude anything sensitive from the backup manifest.

Example
iOS: NSURLIsExcludedFromBackupKey = true on sensitive files
Android: android:allowBackup="false" or a backup rules XML

4. Deep Link & URL Scheme Validation

Description

Validate every deep link and universal/app link against an allowlist before acting on it — including query parameters — since deep links are a common vector for auth bypass and phishing redirects.

Example
if (!ALLOWED_HOSTS.has(url.host)) return // reject
// verify universal-link association, not just a custom scheme

5. Minimal, Justified Permissions

Description

Request only the OS permissions the current feature actually needs, at the point of need (not all at install) — every permission granted is additional attack surface and a privacy question.

Example
Request CAMERA permission only when the user taps
"Scan QR code" — not on app launch

6. Session Expiry & Step-Up Authentication

Description

Access tokens expire and refresh silently for normal use, but sensitive actions (changing a password, adding a payout method) require re-authentication even within an active session.

Example
Viewing balance:        cached session OK
Adding bank account:    force re-auth (biometric/password)

7. Scrub Sensitive Data From Logs & Screenshots

Description

Disable verbose logging in release builds, and blank the screen in the app-switcher/recents view for screens showing balances, PII, or payment details.

Example
Android: FLAG_SECURE on sensitive Activities
iOS: overlay a blank view in applicationDidEnterBackground

8. Vetted Cryptography, Proper Key Management

Description

Use the platform's audited crypto libraries (not a hand-rolled cipher), with keys generated and stored via the secure enclave/hardware keystore rather than embedded as constants.

Example
Android: KeyGenParameterSpec with setIsStrongBoxBacked(true)
when available — not a static byte[] key in source

9. Explicit Consent Before Analytics/Tracking

Description

Analytics and crash-reporting SDKs (Mixpanel, Sentry, Firebase) initialize only after explicit user consent, with PII scrubbed from event payloads and crash breadcrumbs — especially for data involving minors.

Example
if (userConsent.analytics) { Mixpanel.init(token) }
// Sentry: beforeSend() strips email/name from context

10. CSPRNG for Anything Security-Sensitive

Description

OTPs, tokens, and any ID used as an authorization boundary come from a cryptographically secure random source, never Math.random() or a timestamp-derived value.

Example
crypto.getRandomValues(new Uint8Array(16))
// not Math.random()

11. Certificate Pinning for Sensitive Traffic

Description

For high-value API traffic (auth, payments), pin the expected certificate or public key so the app rejects even a validly-signed-but-unexpected certificate — defending against compromised CAs or on-path proxies.

Example
iOS: NSPinnedDomains in Info.plist
Android: Network Security Config <pin-set>

12. Enforce TLS, No Cleartext Exceptions

Description

App Transport Security is fully enforced on iOS and usesCleartextTraffic is false on Android — with no per-domain exceptions carved out for "just this one legacy API."

Example
Android manifest: android:usesCleartextTraffic="false"
iOS: NSAppTransportSecurity with no NSExceptionDomains

13. Biometric Auth Backed by the Secure Enclave

Description

Face ID/Touch ID/fingerprint should gate access to a key stored in the secure enclave — not just show/hide a screen in app code, which a jailbroken device could bypass entirely.

Example
iOS: LAContext + SecAccessControl with
kSecAccessControlBiometryCurrentSet, tied to a Keychain item

14. WebView Hardening

Description

Any WebView loading remote or CMS-sourced content disables unnecessary JS bridges, validates the origin before injecting any native bridge, and never loads arbitrary attacker-influenced URLs.

Example
webView.settings.javaScriptEnabled = false // unless needed
+ shouldOverrideUrlLoading() allowlist check

15. Lock Down Inter-Process Communication

Description

On Android, exported Activities, Services, and Broadcast Receivers are explicitly set to android:exported="false" unless another app genuinely needs to call them, and any that are exported validate their caller.

Example
<activity android:name=".AdminActivity"
android:exported="false" />

16. Dependency & SDK Vulnerability Scanning

Description

Mobile apps bundle dozens of third-party SDKs (analytics, ads, crash reporting) — scan them for known vulnerabilities just like backend dependencies, since they run with the same permissions as your own code.

Example
OWASP Dependency-Check / Snyk for Gradle & CocoaPods
wired into the mobile CI pipeline

17. Strip Debug Symbols From Release Builds

Description

Release builds disable verbose/debug logging, strip debug symbols, and remove any developer backdoors (test endpoints, hardcoded test accounts) that made local development easier.

Example
Android: minifyEnabled true + ProGuard/R8 rules
iOS: strip debug symbols in Release build settings

18. Root/Jailbreak Detection for High-Risk Apps

Description

For apps handling payments or highly sensitive data, detect rooted/jailbroken devices and respond appropriately (warn, restrict features) — one layer among many, not the only defense.

Example
Check for su binary, Cydia, unusual system properties;
restrict payment features on a positive detection

Last updated: September 2026 · Source: Offspring — Security Best Practices.pptx (Part 4) + Security Best Practices Checklist.xlsx