← Blog ·

Dónde NO guardar API keys en una app React Native (y dónde sí)

Si has escrito EXPO_PUBLIC_API_KEY o importado un .env en un componente React Native, probablemente ya tengas un problema de seguridad React Native sin saberlo. El motivo es simple: todo lo que compilas en el bundle de JavaScript viaja al dispositivo del usuario, y ese dispositivo no es de confianza.

Esto no es teórico. Cualquiera con el APK o el IPA puede descomprimirlo y leer el bundle como texto plano. Vamos a ver exactamente dónde falla la gente al gestionar React Native API keys, y qué alternativas funcionan de verdad.

El bundle de JavaScript no es una caja fuerte

Metro y Hermes compilan tu código, pero no lo cifran. Un .env cargado con react-native-dotenv o react-native-config se sustituye en tiempo de build por su valor literal, que queda incrustado en el bundle final. Da igual si tu backend nunca ve el .env: el valor ya está en el .apk que subes a Google Play.

Los sitios donde nunca deberías guardar una API key

1. Variables de entorno empaquetadas en el cliente

EXPO_PUBLIC_*, REACT_APP_* o cualquier variable inyectada por react-native-config termina en texto plano dentro del JS bundle. Sirven para configuración pública (un flag, una URL de API), nunca para secretos.

2. Hardcodeadas en el código fuente

const STRIPE_SECRET_KEY = "sk_live_51H...";

Esto es lo primero que un asistente de IA sugiere si le pides "conecta esto con Stripe", y lo primero que un atacante busca con un grep.

3. AsyncStorage o el sistema de archivos sin cifrar

AsyncStorage guarda datos en SQLite (Android) o un plist (iOS) sin cifrado real. En un dispositivo rooteado o con jailbreak, se lee directamente.

4. Info.plist, strings.xml o google-services.json

Muchos añaden claves de terceros aquí "porque es configuración nativa". Sigue siendo el mismo binario que se distribuye públicamente.

5. El historial de git

Aunque borres la clave del archivo actual, sigue viva en commits anteriores. git log -p | grep sk_ la encuentra en segundos.

Cómo comprobarlo tú mismo

Con un build de release ya generado:

# Android
unzip app-release.apk -d app-release
strings app-release/assets/index.android.bundle | grep -Ei "sk_live|AIza|api[_-]?key"

# iOS
strings MyApp.app/MyApp | grep -Ei "sk_live|AIza"

Si algo aparece, esa clave ya está comprometida en cuanto publicas la app. Revocarla evita nuevo daño, pero no borra las copias que ya se descargaron.

Dónde SÍ deben vivir las API keys

Las claves públicas, en el cliente (sin problema)

Una clave publicable de Stripe, la config de Firebase o un ID de analítica están diseñadas para ser públicas. Ahí el cliente es el sitio correcto.

Los tokens de sesión, en el Keychain/Keystore

Para tokens emitidos tras el login (JWT de corta duración, refresh tokens), usa react-native-keychain o el Keystore nativo. Es cifrado a nivel de sistema operativo y pensado exactamente para esto.

Las claves secretas de terceros: nunca en el móvil

Una API key de OpenAI, la contraseña de tu base de datos o una clave secreta de Stripe no deberían llegar al dispositivo en ningún caso. Si tu app necesita ese servicio, la llamada la hace tu backend, no el cliente.

El patrón correcto: backend + gestor de secretos

La app React Native llama a tu API; tu API llama a OpenAI, Stripe o tu base de datos. El móvil nunca ve esas claves. El problema que queda es: ¿dónde guarda tu backend (y tu CI/CD, y el asistente de IA que escribe el código) esas claves sin que acaben en un .env commiteado?

Aquí es donde entra Koove. En vez de pegar la clave en un archivo, la guardas una vez:

koove set OPENAI_API_KEY sk-proj-xxxx --env prod

El valor se cifra en el cliente con envelope encryption (X25519 + AES-256-GCM) antes de salir de tu máquina; el servidor de Koove solo almacena ciphertext, nunca la clave en claro. Tu código de backend deja de tener el secreto embebido y solo lo referencia por nombre:

import { koove } from "@koove/sdk";

const apiKey = await koove.get("OPENAI_API_KEY");
const client = new OpenAI({ apiKey });

La clave se descifra solo para consumidores verificados: un backend autorizado, o un consumidor móvil validado con App Attest / Play Integrity + biometría, según el caso de uso. Los detalles de configuración están en /docs/sdk.

Sé honesto sobre los límites

Aquí conviene ser claro, no vendedor. Koove protege el secreto hasta el momento de la descarga y descifrado: no puede hacer que un secreto que ya se entregó a un dispositivo comprometido "desaparezca", ni convierte tu app en inhackeable. Lo que sí hace es reducir drásticamente la superficie de exposición: el secreto no vive en tu repo, no vive en el bundle, no vive en texto plano en ningún servidor, y solo llega a quien puede demostrar que es un cliente legítimo. Si tu caso de uso exige que el secreto viaje al propio dispositivo móvil (poco habitual, pero existe), la atenuación con attestation eleva mucho el listón frente a un emulador o una app modificada, pero una vez descifrado en memoria del dispositivo, ese dispositivo lo tiene.

En la era del código generado por IA

Cuando le pides a Copilot o Cursor que "integre Stripe", la sugerencia más probable es una clave hardcodeada directamente en el componente. Es el patrón que más ha visto en su entrenamiento. El asistente de IA puede usar la misma CLI (koove set NAME value) para guardar el secreto, y tu código solo referencia el nombre. Profundizamos en este problema específico en /ai-security.

Checklist rápido

  • ¿Hay algún sk_, AIza, Bearer o contraseña en tu bundle? Compruébalo con strings antes de cada release.
  • ¿Tus claves de terceros pasan por el cliente en algún momento? Deberían pasar solo por tu backend.
  • ¿Guardas tokens de sesión en AsyncStorage? Muévelos al Keychain/Keystore.
  • ¿Tu .env está en .gitignore pero ya fue commiteado antes? Revísalo con git log -p.
  • ¿Tu asistente de IA tiene acceso directo a producir código con secretos en claro? Dale una forma de guardarlos sin exponerlos.

Si quieres dejar de decidir caso por caso dónde va cada clave y centralizar esto con cifrado extremo a extremo, mira los planes en /plans o crea tu cuenta directamente en /register. Si tienes dudas concretas sobre cómo encaja con tu stack, el /faq cubre los casos más habituales.

Código generado por IA

Tu IA escribe el código. ¿Quién guarda los secretos?

El fallo de seguridad más común en apps generadas con IA son las credenciales expuestas. Con Koove, tu propio asistente guarda cada token desde la CLI — cifrado en tu máquina, nunca en el código ni en el repo.