1. Environment variables and secrets leakage
A common finding in Expo security audits is the leakage of API keys. Adding keys to app.json, .env files, or EAS secrets makes them available to the client at runtime. The JavaScript bundle compiles these variables into the package assets:
// VULNERABLE app.config.js
export default {
expo: {
extra: {
paystackSecretKey: process.env.PAYSTACK_SECRET_KEY, // Leak!
}
}
}; The Fix: Never bundle private client secrets. All third-party integrations (like Stripe, Paystack, or database engines) must be proxied through a secure backend API that you control. The app should only store public client keys (like Google Analytics tracking IDs or public payment keys).
2. Securing local storage (AsyncStorage vs. SecureStore)
Expo apps default to AsyncStorage for data persistence. As AsyncStorage does not encrypt data, we can read cached credentials by inspecting the unzipped app container databases during an audit. You must replace AsyncStorage with expo-secure-store for all sensitive tokens:
import * as SecureStore from 'expo-secure-store';
// Secure way to persist tokens
async function saveToken(key, value) {
await SecureStore.setItemAsync(key, value, {
keychainAccessible: SecureStore.WHEN_UNLOCKED_THIS_DEVICE_ONLY
});
} 3. Securing Over-the-Air (OTA) updates
Expo's EAS Update allows pushing JavaScript patches directly to users without App Store reviews. If an attacker gains access to your Expo dashboard, they can deploy a malicious script to all users. To secure updates:
- Enable Code Signing: Configure Expo to sign updates using a private cryptographic key, forcing the client-side app to reject unsigned or modified updates.
- Enforce MFA: Restrict your Expo developer organization to require Multi-Factor Authentication for all developers.
- Limit EAS Access: Apply least-privilege policies to developer keys and CI/CD deployment tokens.
4. Disabling debugging and developer options in production
Ensure that the Expo developer menu and debugging endpoints are disabled in production builds. In your app.json configuration, verify the following keys are set correctly to disable debugging overlays and inspector tools:
{
"expo": {
"jsEngine": "hermes",
"packagerOpts": {
"dev": false
}
}
} OTA update compromise in early stage app
During an audit of a startup fintech built on Expo, we verified that their EAS update setup did not use code signing. An attacker who compromised a developer's API token could have pushed a malicious update injecting keylogging scripts into the payment forms. Fix priority: high. Remediated by implementing mandatory code signing configurations in app.json.
Building your fintech app with Expo? Audit your code before shipping to stores.
Book an Expo Security AuditFrequently asked questions
Are environment variables in app.json/app.config.js secure?
No. Environment variables defined in Expo's configuration or parsed during EAS Build are compiled into the client-side JavaScript bundle. Any reverse engineer can decompile your app and extract them in plain text.
How does Expo SecureStore encrypt data?
Expo SecureStore utilizes Android's KeyStore and iOS's Keychain Services to encrypt values before writing to local disk, making it the secure alternative to AsyncStorage.
What is the security risk of Expo's Over-the-Air (OTA) updates?
If an attacker compromises your Expo developer account or intercepts the update distribution network (MITM), they can push a malicious JavaScript update to all your active users, bypassing App Store and Google Play validation.
Related reading
Blog: React Native pentesting guide · React Native security pitfalls · Hardcoded API keys
Services: Mobile penetration testing · Secure architecture review