Blog
Tutorials
10 Mobile App Security Best Practices for 2026
10 mobile app security best practices with React Native code examples. Covers encryption, authentication, API security, RASP, and dependency scanning for 2026.

Nafis Amiri
Co-Founder of CatDoes

TL;DR: Your mobile app is only as secure as its weakest layer. These 10 practices cover the OWASP Mobile Top 10 threats, from code obfuscation to penetration testing, with implementation guidance for React Native, Expo, and Supabase stacks.
Your users hand over personal data every time they open your app. A single breach can wipe out that trust overnight. According to IBM's 2024 Cost of a Data Breach Report, the average data breach now costs $4.88 million. These 10 mobile app security best practices give you a practical checklist to lock down your React Native app in 2026, with code examples and tool recommendations you can act on today.
Table of Contents
Code Obfuscation and Hardening
Certificate Pinning
Encrypted Local Storage
Multi-Factor Authentication
Secure API Communication
Session Management
Runtime Protection (RASP)
Input Validation and Output Encoding
Dependency Scanning
Penetration Testing
Frequently Asked Questions
Code Obfuscation and Hardening

Without obfuscation, anyone can decompile your APK or IPA, extract API keys, and reverse-engineer your security logic. Code obfuscation renames classes, methods, and variables to meaningless characters, making your compiled app unreadable to attackers.
On Android, R8 handles this during release builds. Add this to your android/app/build.gradle:
release {
minifyEnabled true
shrinkResources true
proguardFiles getDefaultProguardFile(
'proguard-android-optimize.txt'),
'proguard-rules.pro'
}
On iOS, set Strip Swift Symbols and Deployment Postprocessing to "Yes" in your Xcode release build settings.
Save your mapping files. R8 generates a
mapping.txtthat lets you decode obfuscated crash reports. Without it, stack traces are unreadable.Only apply to release builds. Keep debug builds clean for development.
Test performance after enabling. Aggressive obfuscation can occasionally introduce overhead, so profile before shipping.
Certificate Pinning

Certificate pinning hardcodes your server's SSL/TLS public key into the app. Instead of trusting any certificate authority, your app only connects if the server's key matches the one you embedded. This blocks man-in-the-middle attacks, even if an attacker compromises a CA.
On Android, use the Network Security Configuration. Create res/xml/network_security_config.xml with your server domain and Base64-encoded public key hash, then reference it in AndroidManifest.xml with android:networkSecurityConfig="@xml/network_security_config". On iOS, add your domain and key hashes under NSPinnedDomains in Info.plist.
Pin public keys, not certificates. Certificates expire and get re-issued. Public keys are stable across renewals, so your app won't break during rotation.
Always include a backup pin. If your primary key is compromised, the backup lets you rotate without forcing an app update.
Test on real networks. Misconfigured pinning blocks all traffic and makes the app unusable. Verify before release.
Encrypted Local Storage

Storing tokens, credentials, or PII in plain text (whether in AsyncStorage, SharedPreferences, or a standard SQLite database) is a common mistake. If a device is lost, stolen, or rooted, that data is trivially extractable. Verizon's 2024 DBIR found that 68% of breaches involved a human element, and stolen credentials from insecure local storage are a frequent vector.
Use react-native-keychain to store sensitive values in the platform's hardware-backed secure enclave:
import * as Keychain from 'react-native-keychain';
// Store credentials
await Keychain.setGenericPassword('user', token);
// Retrieve credentials
const creds = await Keychain.getGenericPassword();
This uses iOS Keychain Services and the Android Keystore under the hood. For larger datasets like offline databases, use SQLCipher for transparent 256-bit AES encryption.
Never hardcode keys in source code. Generate them at runtime and store them in the Keystore/Keychain.
Encrypt everything sensitive, not just passwords. Session tokens, API keys, and user PII all need protection at rest.
Multi-Factor Authentication

Passwords alone fail constantly. Microsoft reports that MFA blocks 99.9% of automated account compromise attacks. Adding a second factor, something the user has (a phone) or something they are (a fingerprint), turns a single point of failure into layered defense.
For React Native apps with a Supabase backend, enable TOTP (Time-based One-Time Passwords) through Supabase Auth's built-in MFA support. For biometric verification, use expo-local-authentication or react-native-biometrics to prompt for Face ID or fingerprint as a second factor.
Skip SMS-based MFA. SIM-swapping attacks make SMS codes unreliable. TOTP apps (Google Authenticator, Authy) and push notifications are far stronger.
Provide backup recovery codes. Users lose phones. Give them one-time codes to store safely during enrollment.
Re-authenticate for sensitive actions. Require MFA again for password changes, payment updates, or data exports, even within an active session.
Secure API Communication

Data in transit between your app and backend is a prime target. HTTPS is the baseline, but real protection requires authentication, integrity checks, and replay prevention on every request. The OWASP Mobile Application Security project lists "insecure communication" as one of the top mobile risks.
In a React Native app, set up an Axios interceptor to attach the auth token to every outgoing request automatically. Combine this with request signing, adding a cryptographic hash of the request body and timestamp to each call, so your server can verify that requests are genuine and unmodified. You can learn more about how backend-as-a-service platforms handle these patterns out of the box.
Validate timestamps server-side. Reject requests older than 5 minutes to prevent replay attacks.
Rotate API keys and tokens on a schedule. Short rotation windows limit exposure if a key leaks.
Never send sensitive data over plain HTTP. Enforce HTTPS on every endpoint. No exceptions.
Session Management
A stolen session token is as good as a stolen password. Proper session management means short-lived access tokens, secure storage, and a complete cleanup on logout. The standard pattern: issue a short-lived JWT access token (15-60 minutes) paired with a longer-lived refresh token stored in the device's secure enclave.
With Expo and Supabase, store the refresh token using expo-secure-store:
import * as SecureStore from 'expo-secure-store';
await SecureStore.setItemAsync('refreshToken', token);
const stored = await SecureStore.getItemAsync('refreshToken');
Rotate refresh tokens on every use. When a refresh token is exchanged for a new access token, issue a fresh refresh token and invalidate the old one. This detects theft: if an attacker replays the old token, the server rejects it.
Set absolute session limits. Force re-authentication after 24 hours regardless of activity. This protects lost or stolen devices.
Clear everything on logout. Invalidate tokens server-side and wipe all cached session data from the device.
Runtime Protection (RASP)

Static defenses like obfuscation protect your app before it runs. Runtime Application Self-Protection (RASP) monitors the live app from the inside, detecting jailbreak/root access, debugging attempts, code injection, and tampering in real time.
For a quick starting point, use react-native-jail-monkey to detect compromised devices:
import JailMonkey from 'react-native-jail-monkey';
if (JailMonkey.isJailBroken()) {
// Limit sensitive features on compromised devices
}
For production apps handling payments or sensitive data, consider commercial RASP SDKs like Zimperium zDefend or Promon Shield. These provide deeper checks: code integrity verification, anti-debugging, and real-time threat telemetry sent to your backend.
Degrade gracefully. Don't just crash on a rooted device. Disable sensitive features (payments, data export) while keeping basic functionality available.
Log security events. Send detection data to your backend to understand what attacks your app faces in production.
Input Validation and Output Encoding
Every piece of data entering your app (from user forms, API responses, or deep links) is a potential attack vector. SQL injection and XSS attacks exploit apps that trust input without checking it. Client-side validation improves UX by giving instant feedback, but it can be bypassed. Server-side validation is the real defense.
Use Zod in your Supabase Edge Functions for server-side schema validation:
import { z } from 'zod';
const profileSchema = z.object({
username: z.string().min(3).max(20).regex(/^[a-zA-Z0-9_]+$/),
email: z.string().email(),
});
const result = profileSchema.safeParse(await req.json());
if (!result.success) return new Response('Invalid input', { status: 400 });
Whitelist, don't blacklist. Define what good data looks like and reject everything else. Blacklists always miss edge cases.
Use parameterized queries. Supabase's client library and Postgres functions handle escaping automatically. Never build SQL strings by hand.
Encode output for its context. HTML-encode data before rendering in WebViews to prevent XSS.
Dependency Scanning

The average React Native project pulls in hundreds of npm packages. Synopsys found that 84% of codebases contain at least one known open-source vulnerability. A single outdated package with a critical CVE can undermine every other security measure you've built.
Start with the built-in scanner:
npm audit
npm audit fix
For continuous protection, integrate Snyk or GitHub Dependabot into your repository. Both automatically open pull requests when vulnerable dependencies are detected, with suggested safe versions. Add these checks to your CI/CD pipeline so every build is scanned before deployment.
Prune unused packages. Run
npx depcheckperiodically and remove anything you no longer use. Fewer dependencies means a smaller attack surface.Commit your lockfiles.
package-lock.jsonoryarn.lockensures every build uses the exact same dependency versions, preventing surprise transitive updates.Prioritize by CVSS score. Not all vulnerabilities are equal. Fix critical and high-severity issues first.
Penetration Testing
Automated scanners catch known patterns, but they miss business logic flaws, chained vulnerabilities, and creative attack paths. Penetration testing simulates real attacks against your app, APIs, and infrastructure. The OWASP Mobile Application Security Verification Standard (MASVS) provides a structured checklist for what to test.
A practical testing stack for React Native apps:
Static analysis (SAST): MobSF (Mobile Security Framework). Open source, analyzes your APK/IPA for hardcoded secrets, insecure configurations, and code-level vulnerabilities.
Dynamic analysis (DAST): Burp Suite Professional. Intercepts and manipulates live API traffic to test authentication, authorization, and input handling.
Runtime instrumentation: Frida. Hooks into the running app to bypass client-side checks and test server-side enforcement.
Run SAST in your CI/CD pipeline on every build. Schedule manual penetration tests quarterly, or before any major release. Document findings, prioritize by risk, and track fixes to completion. Explore more about how to set up the testing infrastructure in our guide to cross-platform development tools.
Frequently Asked Questions
What is the most important mobile app security practice?
There's no single answer because security works in layers. But if you can only do one thing, encrypt local storage and use platform-native secure enclaves (iOS Keychain, Android Keystore). Stolen credentials from insecure local storage are one of the most common attack vectors in mobile apps.
How do I secure API keys in a React Native app?
Never hardcode API keys in your JavaScript source. They're trivially extractable from the app bundle. Store them server-side and have your app request them through authenticated API calls. For keys that must exist on-device (like push notification tokens), use react-native-keychain or expo-secure-store.
Is certificate pinning necessary for every app?
Not every app needs it. Certificate pinning adds the most value for apps that handle financial data, health records, or authentication tokens. For lower-risk apps, enforcing HTTPS with proper TLS configuration provides a strong baseline. Weigh the added maintenance cost (pin rotation, backup keys) against your threat model.
How often should I test my app's security?
Run automated SAST scans on every CI build. Schedule manual penetration tests at least quarterly and before major releases. Dependency scanning should run continuously. Tools like Dependabot and Snyk monitor your packages around the clock and alert you to new CVEs as they're published.
Does code obfuscation slow down my app?
Modern tools like R8 (Android) and the Swift compiler's optimization passes typically make your app faster, not slower. R8 removes dead code and applies optimizations alongside obfuscation. In rare cases with very aggressive ProGuard rules, you might see minor overhead, so always profile your release build to confirm.
Security isn't a feature you bolt on at the end. It's a foundation you build on from day one. Start with the highest-impact items: encrypted storage, strong authentication, and server-side input validation. Then layer on obfuscation, certificate pinning, dependency scanning, and regular testing. Each practice reinforces the others, and skipping one creates a gap attackers will find.
Ready to build a secure mobile app without configuring all of this from scratch? CatDoes generates production-ready React Native apps with encrypted storage, secure auth, and a protected backend already wired in. Start building today.

Nafis Amiri
Co-Founder of CatDoes


