A single hardcoded API key. A forgotten debug log. A token cached in AsyncStorage instead of the keychain. Any one of these — in an app that took six months to build — can hand an attacker the keys to your users' data on day one of launch.
Mobile is now the dominant attack surface. Industry analysts project mobile-targeted attacks will grow more than 40% by the end of 2026, driven by the explosion of fintech, health, and AI-assistant apps that hold sensitive data on-device. And yet the gap between "we shipped" and "we shipped securely" has never been wider, especially for teams using AI app builders, no-code tools, or React Native templates that abstract the underlying platform away.
This guide is a pragmatic walkthrough of the mobile app security best practices that actually move the needle — ordered roughly by impact-per-hour-of-work, grounded in the OWASP Mobile Application Security Verification Standard (MASVS), and written specifically with React Native and Expo developers in mind.
Photo by FLY:D on Unsplash
What are mobile app security best practices? (40-second answer)
Mobile app security best practices are the controls that protect a mobile app's code, data, and users from theft, tampering, and impersonation. The essential set covers eight areas: secure authentication and session management, encrypted storage of sensitive data, TLS plus optional certificate pinning for traffic, hardened API access with short-lived tokens, no secrets in client code, code obfuscation and tamper detection for high-risk apps, dependency hygiene, and a mobile-specific threat model aligned with the OWASP MASVS framework.
That's the short version. Below is what each one actually looks like in code.
The 2026 Mobile Threat Model in One Picture
Before any checklist, you need a mental model of what you're defending against. A useful simplification: every mobile attack lives in one of four buckets.
| Attack surface | What attackers do | Who's most at risk |
|---|---|---|
| The device | Extract data from local storage, abuse a stolen unlocked phone, exploit jailbroken/rooted OS | Banking, health, enterprise |
| The network | Man-in-the-middle, downgrade TLS, intercept tokens on public Wi-Fi | Any app with auth or payments |
| The app binary | Reverse-engineer the IPA/APK, pull hardcoded secrets, repackage with malicious code | Any app distributing on app stores |
| The backend | Hit your API directly, abuse misconfigured endpoints, replay old tokens | Every app — your client is not your perimeter |
If you can name your top two threats from this table for your product, the rest of this article tells you what to build. If you can't, that's the very first task — the OWASP MASVS gives you a structured way to do it in an afternoon.
1. Authentication and Session Management
Most real-world mobile breaches don't happen because cryptography failed — they happen because authentication failed. Solve this first.
Use a battle-tested identity provider, not your own auth
Roll-your-own auth is one of the few decisions that reliably ages badly. Use Auth0, Clerk, Supabase Auth, Firebase Auth, or AWS Cognito. They handle password hashing, rate limiting, breach detection, and JWKS rotation — all the boring parts that get exploited when DIYed.
Make MFA the default, not the upsell
In 2026, multi-factor authentication is table stakes — not a premium feature. Offer at minimum:
TOTP (Google Authenticator, 1Password, Authy) — universal, free, phishing-resistant when paired with passkeys
Biometric step-up (
expo-local-authentication) — Face ID / Touch ID for re-auth on sensitive actionsPasskeys / WebAuthn — the modern default, supported on iOS 16+ and Android 9+
For React Native, expo-local-authentication wraps both platforms in one API and falls back gracefully when biometrics aren't enrolled.
Treat tokens like cash
Access tokens: short-lived (15 minutes is a sane default), JWT, signed with RS256 or EdDSA — never HS256 with a shared secret.
Refresh tokens: longer-lived but rotated on every use with reuse detection. If a refresh token is ever submitted twice, revoke the entire session family.
Logout must invalidate server-side, not just clear the client. A token sitting in memory after logout is still valid until expiry.
Photo by Jonas Leupe on Unsplash
2. Secure Storage: Don't Put Tokens in AsyncStorage
This is the single most common React Native security mistake. AsyncStorage is a glorified key-value file in your app's sandbox. It's not encrypted on iOS by default and is trivially readable on a jailbroken or rooted device.
For anything sensitive — auth tokens, refresh tokens, encryption keys, PII — use the platform keystore.
Use expo-secure-store (or react-native-keychain)
expo-secure-store wraps the right primitive on each platform:
iOS: stores values in the iOS Keychain as
kSecClassGenericPassword, encrypted by the Secure Enclave and bound to your bundle ID.Android: uses Android Keystore-backed encrypted SharedPreferences. Hardware-backed (TEE / StrongBox) when available.
The API is intentionally tiny:
import * as SecureStore from 'expo-secure-store';
await SecureStore.setItemAsync('refreshToken', token, {
keychainAccessible: SecureStore.WHEN_UNLOCKED_THIS_DEVICE_ONLY,
requireAuthentication: true, // Face ID / Touch ID gate
});
const token = await SecureStore.getItemAsync('refreshToken');
What goes where
| Data type | Storage | Why |
|---|---|---|
| Auth tokens, encryption keys |
expo-secure-store / Keychain |
Hardware-backed, OS-protected |
| User preferences, theme | AsyncStorage |
Non-sensitive, fine |
| Cached API responses (PII) | Encrypted SQLite (e.g., op-sqlite) |
Encrypt at rest with a Keychain-derived key |
| Files (images, documents) | App sandbox + file-level encryption | iOS Data Protection class NSFileProtectionComplete
|
Encrypt sensitive databases
If you cache PII offline — messages, health records, financial data — use SQLCipher or op-sqlite with an encryption key generated and stored in Secure Storage. Never hardcode the database key in source.
3. Data in Transit: TLS, HTTPS, and Certificate Pinning
HTTPS everywhere, no exceptions
Both iOS App Transport Security (ATS) and Android Network Security Config now enforce HTTPS by default. Resist the urge to disable them for "convenience" during development. If you genuinely need cleartext for local dev, scope the exception to a single domain and never ship that config to release builds. React Native's official security docs cover the platform-specific config files.
TLS 1.3, AES-256, and modern cipher suites
You don't usually pick these directly — your platform networking stack does — but you should verify:
TLS 1.3 with TLS 1.2 fallback only
HSTS on your APIs
Forward secrecy via ECDHE
Modern AES-GCM or ChaCha20-Poly1305 cipher suites
Run your endpoint through SSL Labs once a quarter. A score below A is a bug.
Certificate pinning for high-risk apps
Certificate pinning ensures your app only trusts your server's certificate (or its issuing CA), even if a malicious root is installed on the device. In React Native, react-native-ssl-public-key-pinning is the standard library — it pins the public-key hash, which is more rotation-friendly than pinning the leaf cert.
Honest take: pinning has a real maintenance cost. Pins must rotate before your TLS cert does, or you'll brick every installed app. For most consumer apps, enforced HTTPS plus HSTS is enough. Pin if you're handling money, health data, or auth tokens for high-value accounts. And always ship pinning behind an over-the-air update mechanism (expo-updates) so you can rotate without an app-store release.
Photo by Markus Spiske on Unsplash
4. API and Backend Security
Your client is not your perimeter. Every input from the mobile app must be revalidated server-side, every authorization decision made server-side, and every secret kept server-side.
Never put secrets in the bundle
Anything bundled into your IPA or APK is publicly readable. Stripe secret keys, OpenAI keys, Twilio tokens, AWS credentials — all of these belong in your backend, behind your authenticated API. The mobile app calls your server, your server calls the third party.
A scary number of teams break this rule because they're moving fast or because an AI builder wired up the integration wrong. Audit your .env files, your Constants.expoConfig.extra payload, and any process.env.* references in client code before every release.
Use OAuth 2.0 with PKCE
If you call third-party APIs on behalf of the user, use OAuth 2.0 with PKCE. PKCE was designed precisely for native apps that can't keep a client secret. expo-auth-session implements it correctly.
Rate-limit and shape traffic at the edge
Your API will be hit directly — not just from your app. Cloudflare, AWS WAF, or your API gateway should enforce:
Per-IP and per-user rate limits
Bot detection on auth endpoints (signup, login, password reset)
Schema validation rejecting unexpected payloads
Anomaly detection on payment and admin endpoints
Validate everything, trust nothing
Server-side, every request body, query parameter, and header is hostile until proven otherwise. Use a schema library (Zod, Yup, Pydantic) to validate inputs before they reach business logic. Store user IDs from the JWT claim, never from a request field — otherwise a user can simply pass userId: 7 and read someone else's data. (This class of bug, broken object-level authorization, is OWASP API #1 for a reason.)
5. Code-Level Defenses
These matter most for apps that handle high-value assets — banking, crypto wallets, paid streaming — and matter little for a B2B internal tool.
Code obfuscation
JavaScript bundles in React Native are readable. Hermes bytecode raises the bar slightly but isn't real obfuscation. For high-risk apps, layer in:
Minification + property mangling (Metro + Terser settings)
Native obfuscation for any custom native modules (ProGuard / R8 on Android, bitcode + symbol stripping on iOS)
Commercial RASP (Runtime Application Self-Protection) like Guardsquare DexGuard or Promon Shield — overkill for most apps, essential for banking
Jailbreak / root detection
A jailbroken or rooted device throws every device-bound assumption out the window. For sensitive apps, detect and respond:
jail-monkey(React Native) gives you boolean signals for jailbreak, debugger attached, mock location, and emulatorDon't crash the app — that's user-hostile and easy to bypass. Instead, raise the friction (require re-auth, disable offline mode, add server-side anomaly score)
Tamper detection and integrity attestation
Use Apple's DeviceCheck / App Attest and Google's Play Integrity API to verify the app binary hasn't been modified and is running on a genuine device. These are server-validated and much harder to spoof than client-side checks.
Disable debug features in production
Strip
console.logfrom release builds (Metro'sinlineRequires+ Babeltransform-remove-console)Disable React Native debugger and Flipper in release variants
Turn off network inspection tools
Set
__DEV__correctly — don't ship dev-only code paths
6. Dependency and Supply-Chain Hygiene
The npm ecosystem is enormous and hostile. A single malicious post-install script in a transitive dependency can exfiltrate your entire dev environment.
Run
npm audit(or Snyk / Socket.dev) on every PR. Block CI on high/critical findings.Use lockfiles (
package-lock.json,yarn.lock,pnpm-lock.yaml) and never commit edits to them by hand.Pin to specific versions for security-sensitive packages. Floating ranges (
^,~) are fine for utilities but risky for crypto, auth, and networking libs.Scan native dependencies too — CocoaPods and Gradle have their own vulnerability databases.
Maintain an SBOM (Software Bill of Materials) so you can audit exposure when the next big CVE drops.
7. The OWASP MASVS Framework
If you take one thing from this article, take this: align your security work to the OWASP Mobile Application Security Verification Standard. It's the industry standard, free, and structured.
MASVS is split into eight control groups (architecture, storage, crypto, auth, network, platform, code, resilience) and three verification levels:
MASVS-L1 — baseline security every app should meet. Most consumer apps target this.
MASVS-L2 — defense-in-depth for apps handling regulated data (banking, healthcare, government).
MASVS-R — anti-reverse-engineering and resilience controls for apps under active threat.
OWASP also publishes the MASTG (Mobile Application Security Testing Guide) with concrete tests, and a dedicated guide for React Native apps since hybrid frameworks aren't the MASTG's primary focus.
Pick a level based on your threat model, then walk the checklist. It's the closest the industry has to a definitive answer for "are we secure enough?"
Photo by Carlos Muza on Unsplash
People Also Ask
How do you secure a mobile app?
Secure a mobile app by layering controls across four surfaces: device (encrypted secure storage, biometric step-up, jailbreak detection), network (HTTPS with TLS 1.3, optional certificate pinning), binary (code obfuscation, tamper detection, no hardcoded secrets), and backend (short-lived JWTs, OAuth 2.0 with PKCE, server-side authorization). Map each control to OWASP MASVS L1 as your minimum bar.
What is the OWASP MASVS?
The OWASP Mobile Application Security Verification Standard (MASVS) is the industry-standard framework for mobile app security requirements. It defines eight control groups across architecture, storage, cryptography, authentication, network, platform, code quality, and resilience, with three verification levels — L1 baseline, L2 defense-in-depth, and R for reverse-engineering resistance. It's free, maintained by OWASP, and pairs with the MASTG testing guide.
Is React Native secure?
React Native is as secure as the patterns you use with it. The framework itself ships safe defaults — HTTPS-by-default, sandboxed storage, native crypto primitives — but common mistakes like storing tokens in AsyncStorage, hardcoding API keys in Constants.expoConfig.extra, or shipping debug builds undermine all of that. Follow OWASP MASVS, use expo-secure-store, and you can match the security of native apps.
What AI App Builders Get Right (and What's Still on You)
If you're building with an AI app builder like RapidNative — which generates production-ready React Native and Expo code from natural-language prompts — a real chunk of the security baseline comes pre-wired. Generated code uses Expo's modern SDK defaults: expo-secure-store instead of AsyncStorage for tokens, HTTPS-enforced fetch wrappers, environment-aware config, and modern auth patterns when you connect Supabase or Firebase. Our export pipeline ships clean, auditable code you fully own — no lock-in, no black boxes.
But — and this is the honest part — no AI builder, no template, and no boilerplate can decide your threat model for you. Whether you need certificate pinning, jailbreak detection, or App Attest depends on what your app does and who wants to attack it. The same applies to vibe-coded apps in general; we wrote about the risks and best practices of vibe coding for exactly this reason. Treat AI-generated code as a strong starting point — then run it through the MASVS checklist before you ship.
A Pre-Launch Mobile App Security Checklist
Print this. Pin it above your monitor. Walk it before every release.
| # | Control | Quick check |
|---|---|---|
| 1 | No hardcoded secrets in the bundle |
grep your build output for sk_, Bearer, AWS keys |
| 2 | Tokens in expo-secure-store, not AsyncStorage
|
Audit storage calls |
| 3 | HTTPS enforced, ATS / NSC unmodified | Check Info.plist and network_security_config.xml
|
| 4 | Short-lived access tokens + refresh rotation | Test refresh-token reuse triggers session revocation |
| 5 | OAuth 2.0 with PKCE for third parties |
expo-auth-session with usePKCE: true
|
| 6 | Server-side authorization on every endpoint | Try IDOR with a second test account |
| 7 | Rate limiting on auth endpoints | Hammer login with curl; expect 429 |
| 8 |
console.log stripped in release builds |
Inspect the release bundle |
| 9 |
npm audit clean (no high/critical) |
CI gate |
| 10 | Dependencies pinned, lockfile committed |
git status clean after npm ci
|
| 11 | Privacy manifest + permissions justified | iOS 17+ requires PrivacyInfo.xcprivacy
|
| 12 | Crash reporting scrubs PII | Sentry beforeSend filter |
| 13 | App Attest / Play Integrity wired (if high-risk) | Verify server-side |
| 14 | MASVS L1 self-assessment passed | OWASP MAS checklist |
Closing Thought: Security Is a Feature, Not a Phase
The biggest shift in mobile security thinking since 2020 is that "we'll add security at the end" no longer works. Modern apps move faster — fueled by AI builders, faster CI, and OTA updates — which means security has to move with them, not after them. Build the right defaults in from day one, align to MASVS, and revisit the checklist before every release.
The good news: the tooling is dramatically better than it was even two years ago. Expo's secure primitives, modern identity providers, integrity APIs, and AI builders that ship safe defaults out of the box mean a small team can ship something genuinely well-secured without a dedicated security engineer.
If you're starting a new React Native or Expo app and want safe defaults baked in from the first prompt, try RapidNative free — describe your idea, get production-ready code, and iterate from there. Pair it with this checklist, and you'll be ahead of the security curve where most launches are still behind.
Want to go deeper on the React Native side? Read What is React Native, and our Expo vs React Native
Top comments (0)