Understanding OWASP M1 (2024): Improper Credential Usage in React Native/Expo and How to Mitigate It


Improper Credential Usage (M1) tops the OWASP Mobile Top 10 (2024) because it hits the core of mobile security: protecting secrets and sensitive data. This vulnerability occurs when apps mishandle credentials — whether hardcoded API keys, tokens, or user authentication data — within insecure client environments.

For React Native and Expo developers, this issue is particularly severe. Since the JavaScript bundle ships with the app, anyone with basic reverse-engineering tools can easily peek into the source, exposing credentials you thought were “hidden.”

Let’s break down what this means for you — and how to fix it properly.



Understanding M1: The Risk of Client-Side Credential Exposure

Improper Credential Usage arises when developers treat the mobile client like a private backend. The problem? Your app code runs entirely on the user’s device, meaning anything embedded in it — even environment variables — can be extracted from the .apk or .ipa file.



Common M1 Scenarios



A. Hardcoded Application Secrets

These are non-user credentials that give wide access to your system:

  • Firebase Admin keys
  • Stripe secret keys
  • Private API keys
  • Database credentials

Many developers think .env files or libraries like react-native-dotenv keep secrets safe. They don’t. These variables are bundled into the final JS code — anyone with reverse-engineering tools can extract them in plain text.



B. Insecure Storage of User Credentials

On the other hand, storing sensitive user data (access tokens, refresh tokens, biometric keys, etc) in insecure storage like AsyncStorage makes it easily retrievable — especially on rooted or jailbroken devices.



Mitigation I: Protecting Application Secrets (Backend-for-Frontend Pattern)

Golden rule: Never embed app-level secrets in your mobile code.

The best solution is to isolate secrets using a Backend-for-Frontend (BFF) proxy or serverless orchestration layer.

Principle Recommended Action
Architectural Isolation Build a secure backend endpoint that your mobile app talks to. The backend makes the actual third-party API calls using stored credentials.
Serverless Ready Use serverless functions (AWS Lambda, Google Cloud Functions, or Cloudflare Workers) for lightweight, scalable orchestration.
Secrets Management Store sensitive keys in AWS Secrets Manager, Google Secret Manager, or encrypted environment variables — never in the app.
Client Update Change your React Native code to call your secure proxy instead of external APIs directly.

This ensures your app remains a “dumb client,” relying on your backend for anything that needs secret keys.



Mitigation II: Protecting User Credentials (Expo SecureStore)

When user credentials must live on the device (like tokens for login sessions), they should be stored using hardware-backed secure storage, not in plain JS-accessible stores like AsyncStorage.



✅ Recommended Tool: expo-secure-store

Expo SecureStore is a unified interface over native OS storage systems — Keychain Services (iOS) and Android Keystore. These systems are designed by Apple and Google to safely store small bits of sensitive data like passwords, tokens, and encryption keys.

Platform Underlying System Description
iOS Keychain Services Data is stored in the device’s Secure Enclave — a hardware chip isolated from the OS. It requires user authentication (Face ID, Touch ID, or passcode) before access.
Android Android Keystore System Credentials are stored in an isolated keystore. Private keys never leave this secure hardware zone; even the app can’t read them directly. Data is encrypted with AES before being persisted.



📱 Example Implementation

import * as SecureStore from 'expo-secure-store';

// Save user token securely
async function saveUserToken(token) {
  await SecureStore.setItemAsync('userToken', token);
}

// Retrieve stored token
async function getUserToken() {
  return await SecureStore.getItemAsync('userToken');
}
Enter fullscreen mode

Exit fullscreen mode

This ensures your sensitive data lives in encrypted, OS-managed storage — not in plain JS memory or AsyncStorage. Learn more for detailed implementation



How Expo SecureStore Works with Keychain & Keystore

Behind the scenes:

  • On iOS, expo-secure-store communicates with the Keychain API, which stores your key-value pair inside the Secure Enclave chip. The OS encrypts it with a device-specific key, ensuring only your app (or authenticated user) can access it.
  • On Android, it interacts with the Keystore System, where a master key is generated inside the Trusted Execution Environment (TEE). This master key encrypts your stored values. Even if the device is compromised, attackers cannot export these keys.

This native integration means data is not only encrypted but hardware-isolated.



Final Thoughts

Improper Credential Usage (M1) isn’t just about bad code — it’s about architectural decisions. Every time a secret key or token sits in your mobile code, you’re one decompilation away from a breach.



TL;DR for React Native/Expo developers:

  • ❌ Never hardcode secrets or rely on .env in the app.
  • ✅ Route API calls through a secure backend proxy.
  • ❌ Don’t use AsyncStorage for tokens.
  • ✅ Use expo-secure-store or react-native-keychain to leverage native OS secure storage.

Securing credentials isn’t just a best practice — it’s what separates a secure app from a data breach headline.



Source link