The hidden cost of developer convenience
Engineering teams choose Expo because it removes the pain of managing Xcode and Android Studio. You write Javascript or Typescript once, and Expo handles the brutal complexity of compiling native iOS and Android binaries. This abstraction saves hundreds of hours of engineering time.
But that convenience creates a massive security blind spot. When you do not control the native compilation process, you often forget how the native operating system handles your data. Developers treat their Expo application like a web application. They bundle secrets into the frontend. They store authentication tokens in plain text. They push unverified Javascript updates directly to user devices.
Hackers do not treat your Expo app like a web app. They download your compiled APK or IPA. They extract the Javascript bundle. They read your uncompiled business logic in plain text. We perform aggressive Expo penetration testing to find these exact flaws before the hackers do. Here is exactly what we test, and how you must fix it.
Building your fintech app with Expo? We find the vulnerabilities that automated scanners miss.
Book an Expo Security Audit1. 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 (Expo Application Services) 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!
}
}
}; Many developers believe that using `.env` files hides their secrets from the final build. This is entirely false in the context of mobile applications. When Expo builds your Javascript bundle, it replaces `process.env.PAYSTACK_SECRET_KEY` with the actual string value of that key.
We download your published application. We run a simple string extraction tool on the `index.android.bundle` file. We pull out your AWS keys, your database passwords, and your third-party payment secrets. We use those keys to compromise your backend infrastructure directly.
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). Use EAS Secrets exclusively for build-time keys (like Apple Developer certificates), never for runtime application logic.
2. Securing local storage (AsyncStorage vs. SecureStore)
Expo apps default to AsyncStorage for data persistence. This is a critical mistake for fintech applications. AsyncStorage does not encrypt data. It writes your data to a plain text SQLite database or a set of JSON files in the application's local directory.
If a user loses their phone, or if they install a malicious application that exploits a sandbox escape, attackers can read the AsyncStorage files directly. We regularly find JWT authentication tokens, biometric bypass flags, and plaintext user passwords sitting in AsyncStorage.
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
});
} Expo SecureStore utilizes Android's KeyStore and iOS's Keychain Services to encrypt values before writing them to the local disk. When you use the `WHEN_UNLOCKED_THIS_DEVICE_ONLY` flag on iOS, the token is destroyed if the user restores their iPhone from a backup, and it cannot be accessed until the user unlocks their phone with FaceID or their passcode. This is the only acceptable way to store authentication data on a mobile device.
3. Securing Over-the-Air (OTA) updates
Expo's EAS Update allows pushing JavaScript patches directly to users without App Store reviews. This feature is incredibly powerful for fixing critical bugs quickly. However, if an attacker gains access to your Expo dashboard, they can deploy a malicious script to all users. They can push an update that steals user passwords and sends them to an external server.
We audit your EAS Update configuration. We check if you allow unsigned code execution. To secure updates, you must implement strict cryptographic controls:
- Enable Code Signing: Configure Expo to sign updates using a private cryptographic key, forcing the client-side app to reject unsigned or modified updates. Generate this key locally and never upload it to Expo's servers.
- Enforce MFA: Restrict your Expo developer organization to require Multi-Factor Authentication for all developers. A compromised developer password should never lead to a compromised user base.
- Limit EAS Access: Apply least-privilege policies to developer keys and CI/CD deployment tokens. Your GitHub Actions runner should only have permission to publish to the staging channel, never directly to production.
4. Disabling debugging and developer options in production
Ensure that the Expo developer menu and debugging endpoints are disabled in production builds. If you leave these enabled, attackers can connect directly to the React Native bridge. They can read the Redux state. They can intercept network requests before they encrypt. They can inspect your component hierarchy and find hidden administrative menus.
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
}
}
} You should also ensure you compile your Javascript using the Hermes engine. Hermes compiles your Javascript into optimized bytecode. While bytecode can still be reverse engineered, it is significantly harder to read than plain text Javascript. It adds a crucial layer of defense in depth.
OTA update compromise in early stage app
During a penetration test, we found an unsigned update channel that allowed us to push arbitrary Javascript to the client application. We generated a malicious EAS update that hijacked the login form and captured the user's plain text password before sending it to the legitimate authentication endpoint. Because the app did not enforce update signing, the client device accepted our malicious code immediately. Fix: Require update signing, limit publishing access, separate channels, and test that the app rejects a changed manifest or bundle.
5. Bypassing SSL Pinning in Expo
If your Expo app handles financial transactions, you must implement SSL pinning. SSL pinning ensures your app only talks to your specific backend server, preventing Man-in-the-Middle (MITM) attacks even if the user installs a malicious Root CA certificate on their phone.
However, implementing SSL pinning in managed Expo apps used to be impossible because it required writing custom native code. Now, you can use Expo Config Plugins to inject the pinning configuration into the native Android and iOS projects during the prebuild phase. We test your implementation by intercepting the traffic using Burp Suite and Frida. We verify if your app properly terminates the connection when presented with an invalid certificate.
Vulnerability Assessment for Expo and Hybrid Architectures
A traditional mobile vulnerability assessment relies heavily on static code analysis of Swift or Kotlin. A vulnerability assessment for an Expo application requires a completely different approach. We bypass the native shell and aggressively decompile the Hermes bytecode. We analyze the app.json manifest for misconfigured intents, deep links, and scheme hijacks. We map out the entire Javascript-to-Native bridge. You cannot use a generic scanner for an Expo vulnerability assessment; it will simply flag outdated React dependencies and completely miss the logic flaws in your EAS Build configuration.
Expo Pen Testing vs React Native Pen Testing
Standard React Native pentesting focuses extensively on the Javascript bridge and custom native modules. Expo pen testing requires auditing the managed infrastructure. When we conduct an Expo pen test, we explicitly target the Expo Application Services (EAS) deployment pipeline. We attempt to compromise your EAS Update channels, bypass your OTA signing keys, and exploit misconfigurations in the Expo Go client environment. Expo pen testing is an infrastructure test just as much as it is an application test.
Cloud Security for Hybrid Environments in Expo
Expo applications are inherently hybrid, often relying heavily on Backend-as-a-Service (BaaS) platforms like Firebase, Supabase, or AWS Amplify. Cloud security for hybrid environments running Expo is absolutely critical. We routinely find Expo apps that bundle administrative Firebase Service Account JSON keys directly into the client bundle. We find Supabase anonymous keys deployed alongside misconfigured Row Level Security (RLS) policies, allowing us to query the entire user database directly from the Expo frontend. You must decouple your Expo application from your cloud database by implementing a strict middleware API layer.
Release gate for an Expo build
Test the exact Android App Bundle and iOS archive that will ship. Record the build profile, runtime version, update channel, signing identity, package hash, and native permissions. Install an older build, publish a test update, roll it back, and confirm incompatible code never reaches the wrong runtime.
Keep the update manifest, signature result, channel rules, device log, and API request that proves the app accepted or rejected the update. Pair this checklist with our React Native penetration testing guide for bundle, bridge, storage, and API tests.
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 · Reverse engineering