-
Most mobile breaches aren't sophisticated. They're a hardcoded API key, a forgotten debug flag, or a token in plaintext
AsyncStorage. - The OWASP Mobile Top 10 (2024) is your checklist β work it every release.
-
Tokens live in the OS keychain via
expo-secure-store, neverAsyncStorage. Period. - Certificate pinning for sensitive endpoints, pinned to the SPKI hash, with a backup pin and a rotation plan.
- AI-generated code is untrusted input β review auth, storage, and network code with the same rigor as a new contributor's PR.
-
CI is where security lives:
semgrep+eslint-plugin-security+npm audit+ MobSF on every release artifact.
Mobile attacks are up. Regulators are watching. AI is writing more of your code than ever β and the patterns it reproduces aren't always the secure ones. Here's the practical checklist I run through for every React Native / Expo app, organized around the OWASP Mobile Top 10 (2024).
This is the working version of a longer guide β focused on what to actually change in your codebase this week.
1. Credentials: nothing sensitive in the bundle, nothing sensitive in AsyncStorage
// β Don't
await AsyncStorage.setItem('access_token', token);
// β
Do
import * as SecureStore from 'expo-secure-store';
await SecureStore.setItemAsync('access_token', token, {
keychainAccessible: SecureStore.WHEN_UNLOCKED_THIS_DEVICE_ONLY,
});
Anything in the bundle can be extracted with apktool in minutes. Anything in AsyncStorage is plaintext on disk. Tokens go in the OS keychain via expo-secure-store or react-native-keychain. Period.
Refresh tokens rotate on every use. Access tokens live 15 minutes. The backend is the trust boundary, not the client.
2. Supply chain: assume your dependencies are hostile
# In CI, on every PR
npm ci
npm audit --audit-level=high
A clean package.json doesn't mean a clean app. Post-install scripts run with your dev-machine privileges. Native modules run with full app privileges.
- Lockfiles in source control.
npm ciin CI, nevernpm install. - Add Socket or Snyk for behavioral analysis (what npm audit misses).
- Audit native modules personally if they touch storage, networking, or credentials.
3. Auth: PKCE, short JWTs, server-side authorization
import * as AuthSession from 'expo-auth-session';
// PKCE is the default in expo-auth-session β don't disable it.
const request = new AuthSession.AuthRequest({
clientId,
scopes: ['openid', 'profile'],
usePKCE: true,
redirectUri,
});
OAuth 2.0 with PKCE for third-party identity. JWTs with 15-minute access tokens and rotated refresh tokens. Every endpoint validates the caller server-side. Hiding UI is not authorization.
Add MFA via expo-local-authentication for anything touching payments, identity, or health data.
4. Input validation: deeplinks are user input
// Treat the URL params as hostile
const handleDeepLink = (url: string) => {
const parsed = new URL(url);
const action = parsed.searchParams.get('action');
if (action && /^[a-z_]{1,32}$/.test(action) && KNOWN_ACTIONS.has(action)) {
routeTo(action);
}
};
Deeplinks, push payloads, clipboard, QR codes, WebView messages β all untrusted. Validate type, length, format. Parameterized queries for local SQLite. originWhitelist on every WebView.
5. TLS 1.3 + certificate pinning
<!-- android/app/src/main/res/xml/network_security_config.xml -->
<network-security-config>
<domain-config cleartextTrafficPermitted="false">
<domain includeSubdomains="true">api.yourapp.com</domain>
<pin-set>
<pin digest="SHA-256">{base64-spki-hash}</pin>
<pin digest="SHA-256">{backup-spki-hash}</pin>
</pin-set>
</domain-config>
</network-security-config>
Pin to the SPKI hash, not the leaf cert. Ship a backup pin. Have a rotation plan. Reject TLS 1.0/1.1 server-side.
In Expo, usesCleartextTraffic: false. Verify no allowsArbitraryLoads snuck into production.
6. Privacy controls aren't optional
Maintain a data inventory. Apply data minimization. Request permissions just-in-time with context. Build account-delete-and-export flows that actually delete and export. Audit analytics/ad SDKs quarterly β they change practices on their schedule.
7. Binary hardening: Hermes, R8/ProGuard, App Attest
- Ship Hermes bytecode β much harder to reverse than plain JS.
- R8 with shrinking and obfuscation on Android.
-
babel-plugin-transform-remove-consolein release builds. -
jail-monkeyfor rooted/jailbroken detection (signal, not block). - Apple's App Attest + Google's Play Integrity API to verify the app calling your backend is the one you shipped.
8. Configuration hygiene
-
__DEV__guards on every debug code path. -
android:exported="true"only when truly needed. - URL schemes and Universal Links audited as entry points.
- CI check that fails the build if known test-account strings hit the binary.
9. Encrypted storage, intentional sensitivity tiers
| Sensitivity | Storage |
|---|---|
| Credentials, keys | iOS Keychain / Android Keystore via expo-secure-store
|
| Structured PII | SQLCipher or encrypted Realm |
| Non-sensitive | Regular filesystem or AsyncStorage
|
Disable backup for sensitive paths. Mask app-switcher screenshots on sensitive screens via expo-screen-capture.
10. Modern crypto, vetted libraries
import { randomBytes } from 'react-native-quick-crypto';
// AES-256-GCM. Never CBC without auth. Never ECB. Ever.
Argon2id for password hashing. HKDF for key derivation. SHA-1 and MD5 are dead. Start tracking your post-quantum migration β NIST's ML-KEM and ML-DSA are finalized.
11. Treat AI-generated code as untrusted input
This is the one most security frameworks haven't caught up to. LLMs reproduce the most common pattern in their training data β often the most common flawed pattern.
- Maintain a
.cursorrulesor.github/copilot-instructions.mdwith your secure defaults. - Review AI-generated auth, storage, and network code with the same rigor as a new contributor's PR.
- Run
semgrepandeslint-plugin-securityon AI output before merge. - Pen-test AI-generated payment and auth flows specifically.
12. CI is where security lives
Every PR:
-
semgrep,eslint-plugin-security, Android Lint -
npm audit+ Socket/Snyk - MobSF on release-build artifacts
Annually: pen test. Quarterly: SDK audit. Always: an incident response plan that includes key rotation, token revocation, and an OTA push.
Most mobile breaches aren't sophisticated. They're a hardcoded key, a forgotten debug flag, a plaintext token. The OWASP Top 10 is your checklist β work it every release.
For the longer breakdown β including the AI-safe-code generation patterns and the full OWASP-mapped CI workflow β see the RapidNative blog.
What's the security gotcha you've shipped to production and then quietly patched? Drop it in the comments β I'm collecting the failure modes that don't make it into the OWASP examples for a follow-up.
United States
NORTH AMERICA
Related News
π I Built a Dropshipping Automation Pipeline β Here's What I Learned (and What I'd Do Differently)
10h ago
How I Cut My LLM API Bill by 40x: A Freelancer's Migration Story
10h ago

Mattress Firm Coupons: Save up to $600
3h ago
Google Ordered to Pay $2 Billion For Anti-Competitive Practices By Swedish Court
20h ago
The Censorship Wall: Why Every AI Companion App Ends Up Filtering You
20h ago