-
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
Secret Claude Tracker Shocks Users After Anthropic's Anti-Surveillance Stance
12h ago
EV Batteries Defy Expectations, Last Hundreds of Thousands of Miles
1d ago
GBase 8a Performance Anomaly Case Study: How a Single Parameter Change Sparked a Chain Reaction
1d ago
Who Else Has Inherited a Codebase With Zero Comments and a Prayer?
1d ago
ๅฎ็พ็ๅนณๅบธ
4h ago