Claves API en builds frontend: cómo detectar y arreglar filtraciones
Las claves API en compilaciones frontend pueden exponer tus datos. Aprende a encontrar secretos incluidos en el bundle, moverlos al servidor y rotarlos de forma segura.

Qué significa filtrar secretos a través de una compilación frontend
Una compilación frontend es el conjunto de archivos que tus usuarios descargan para ejecutar tu app en un navegador. Normalmente incluye JavaScript empaquetado (a menudo un archivo grande), CSS, imágenes y otros assets estáticos. Las herramientas de build toman tu código fuente y lo empaquetan para que el navegador lo cargue rápido.
Hay una filtración cuando algo que pretendías mantener privado se incrusta en esos archivos descargables. Si se envía al navegador, cualquiera puede verlo mirando el código fuente, abriendo DevTools o descargando el bundle y buscándolo.
Los secretos son cualquier cosa que permita a alguien actuar como tu app o acceder a sistemas protegidos. Ejemplos comunes: claves API y tokens de servicio (Stripe, OpenAI, proveedores de mapas, servicios de email), cadenas de conexión de bases de datos, secretos de firma JWT, claves privadas de cifrado, URLs privadas de webhooks, endpoints de administración y URLs internas que eluden la autenticación normal.
Por eso las claves API en compilaciones frontend deben tratarse como información pública. La minificación y la ofuscación no ayudan. Si el navegador debe recibir el valor para usarlo, un atacante también puede recibirlo.
El impacto suele ser directo: alguien copia tu clave y te genera facturas, agota límites de tasa para que los usuarios reales tengan errores, o usa tu cuenta para spamear a un proveedor. Si el secreto desbloquea acceso a datos, puede convertirse en una filtración real (registros de clientes, archivos o acciones administrativas). Los costes y daños suelen comenzar pequeños y escalar rápido, sobre todo cuando la clave filtrada tiene permisos amplios.
Formas comunes en que los secretos terminan en código cliente
La mayoría de las filtraciones no son “hacks”. Vienen de decisiones normales de build y configuración que tratan los secretos como ajustes públicos por accidente.
La inyección en tiempo de compilación convierte .env en código enviado
Herramientas como Vite, Next.js y Create React App pueden reemplazar variables de entorno en tiempo de compilación. Si una clave se referencia en código cliente, el bundler a menudo la inserta como una cadena literal. Un valor que vivía seguro en un archivo .env local puede acabar incrustado en el JavaScript de producción.
Una trampa común es pensar “está en una env var, así que es seguro”. Solo es seguro si se usa en el servidor y nunca se incluye en los bundles cliente.
Los prefijos “públicos” de env son para valores públicos
Los frameworks usan prefijos para marcar variables que pueden exponerse al navegador (por ejemplo: VITE_, NEXT_PUBLIC, REACT_APP). Son adecuados para configuraciones no sensibles como un flag de característica o un ID público de analítica.
No son un lugar para nada que pueda gastar dinero, acceder a datos privados o conceder permisos elevados: claves de terceros que generan cargos, tokens de administrador, claves de firma, URLs de bases de datos, credenciales de servicio y secretos de webhooks.
Si pones un secreto tras un prefijo público, estás pidiendo explícitamente al sistema de build que lo publique.
Cadenas hardcodeadas y archivos de configuración “útiles”
Los secretos también se cuelan mediante constantes, objetos de configuración y archivos JSON (por ejemplo, config.ts, firebaseConfig o settings.json). Pueden parecer configuración inocua durante la revisión.
Esto es especialmente común en prototipos generados por IA: las herramientas a menudo pegan un ejemplo funcional con una clave en línea para que la demo “simplemente funcione”. Si ese código se envía, la clave se hace pública.
Los source maps facilitan su descubrimiento
Los source maps no crean la filtración, pero pueden hacerla mucho más fácil de encontrar. Si los source maps son públicos, un atacante puede buscar en el código legible tokens, endpoints y cadenas que parezcan secretos en lugar de escanear bundles minificados.
Si sospechas que un secreto se ha expuesto, asume que ya está comprometido. Planea moverlo al servidor y rotarlo.
Formas rápidas para detectar vars de entorno y claves API expuestas
A menudo puedes confirmar una filtración en minutos sin cambiar código. Empieza con lo que ya tiene el navegador.
-
Observa las solicitudes de red en DevTools. Abre la pestaña Network y recorre flujos que llamen a servicios de terceros (login, checkout, búsqueda, subida de archivos). Abre una petición y revisa Headers y Preview/Response. Las filtraciones suelen aparecer como query params (por ejemplo
?api_key=), una cabeceraAuthorization: Bearer ...o un body JSON que incluya un token. -
Busca en lo que realmente envías. En la carpeta de salida compilada (a menudo
dist/obuild/), ejecuta una búsqueda de texto en archivos.jspor patrones comosk-,api_key,apikey,secret,token,Bearer,BEGIN PRIVATE KEY, además de nombres de proveedores y tus URLs base (Stripe, OpenAI, Twilio, Supabase, etc.). -
Abre el bundle compilado y escanea cadenas sospechosas. Busca credenciales hardcodeadas, endpoints completos o blobs de configuración que incluyan claves. Un indicador común es configuración en tiempo de ejecución expuesta en
window(por ejemplowindow.__CONFIG__owindow.env) que incluya algo más que IDs públicas. -
Comprueba si los source maps son accesibles en producción. Los archivos
*.mappúblicos pueden revelar nombres de variable y estructuras de configuración originales, lo que acelera el abuso.
Una prueba práctica: si puedes copiar un token desde DevTools y reproducir la misma petición desde otro perfil de navegador o un script, no es un valor seguro del lado del cliente.
Planifica la limpieza antes de cambiar código
Una vez confirmada la filtración, no empieces a eliminar variables ni a rotar todo a la vez. Un poco de planificación evita caídas, logins rotos y errores 401 confusos.
Empieza listando cada sistema externo con el que tu app habla y dónde viven hoy las credenciales: pagos, email, almacenamiento de archivos, analítica, APIs de IA y cualquier otra cosa. Incluye staging, preview y desarrollo local. Las filtraciones suelen ocurrir en builds de preview porque parecen descartables.
Luego separa lo que puede ser llamado desde el navegador de lo que debe ser únicamente servidor-side. Todo lo que pueda gastar dinero, leer datos privados, escribir en tu base de datos o eludir límites de tasa no debería ser invocable directamente desde el cliente. Si un servicio ofrece una clave publicable para navegadores, trátala diferente a una clave secreta.
Crea un inventario simple que el equipo pueda compartir:
- Nombre de la clave tal como aparece en el código (por ejemplo,
STRIPE_SECRET_KEY) - Servicio y cuenta/proyecto al que pertenece
- Entornos (dev, staging, prod)
- Dónde aparece (repositorio, CI, ajustes de hosting, archivos compilados)
- Nivel de riesgo (bajo para IDs públicas, alto para secretos)
Después decide qué rotar de inmediato y qué rotar después de cambiar el código. Rota ahora si la clave filtrada concede acceso de escritura, tiene scopes amplios o puede consumir créditos. Retrasa la rotación si rompería producción antes de tener un reemplazo servidor-side.
Establece una ventana corta de congelación (incluso 30–60 minutos) donde nadie haga merges, despliegues o cambie env vars de hosting mientras coordinas. Asigna un responsable por servicio y acordad el orden: desplegar cambios servidor-side primero, rotar después y luego redeploy y verificación.
Paso a paso: audita, quita y retesta tu build
Trata esto como un incidente: encuentra lo filtrado, detén la filtración y prueba que se ha ido.
1) Audita lo que existe (repositorio y salida de build)
Busca en tu código fuente y en los assets compilados (los archivos finales que los usuarios descargan). No te quedes solo en el repo. Los archivos de build pueden esconder secretos en bundles minificados.
Un flujo sencillo:
- Escanea por patrones tipo secreto: tokens largos, prefijos de proveedor (por ejemplo
sk_,xoxb-) y cadenasBearer. - Revisa el uso de env:
process.env,import.meta.envy cualquier variable pública del framework. - Busca en el JS compilado cadenas obvias: tu dominio API, prefijos de clave o blobs JSON con credenciales.
- Confirma dónde aparece: en el código fuente, en la config o solo después del paso de build.
- Anota cada coincidencia y dónde se encontró antes de cambiar nada.
2) Confirma qué es realmente sensible
No todo lo que parece una clave es peligroso. Algunos servicios usan claves publicables para el navegador, mientras que otras dan acceso total a la cuenta.
Hazte dos preguntas:
- ¿Qué puede hacer esta credencial?
- ¿Puede un atacante usarla desde su propia máquina?
Si la respuesta es sí y sí, trátala como sensible.
3) Elimínala del cliente y reemplaza por una llamada al servidor
Borra el secreto del código cliente y de cualquier env en tiempo de compilación que acabe en el navegador. Sustituye la llamada cliente→proveedor por un endpoint en el servidor que use el secreto en servidor. El frontend debe llamar a tu endpoint, no al proveedor directamente.
4) Retesta con una clave nueva (primero no-producción)
Crea una clave nueva, prueba de extremo a extremo en staging o preview y solo entonces cambia producción. Rebuild y vuelve a escanear la salida para confirmar que el secreto ha desaparecido.
Mover secretos al servidor sin romper la app
La solución más segura no es “ocultarla mejor en JavaScript”. Es dejar de enviar el secreto al navegador por completo.
El patrón básico: un proxy en el servidor
Haz que el cliente llame a un endpoint de tu backend (o a una función serverless). Ese endpoint llama a la API de terceros y añade el secreto en el servidor. El navegador nunca ve la clave.
Un enfoque simple que mantiene el mismo comportamiento de UI:
- Sustituye la llamada directa al API de terceros en el frontend por una llamada a tu endpoint.
- En el servidor, lee el secreto desde variables de entorno que solo existen en servidor.
- Devuelve únicamente los datos que la UI necesita, no la respuesta completa upstream.
Evita construir un proxy abierto. Valida las entradas en el servidor: permite solo las rutas que necesitas, comprueba los campos requeridos y bloquea URLs o cabeceras inesperadas. Si tu endpoint acepta una “URL objetivo” desde el cliente, has recreado la filtración en nueva forma.
Prefiere tokens de corta duración cuando sea posible
Si un tercero lo soporta, genera tokens de corta duración en el servidor y envía solo el token al cliente. Tokens que expiran en minutos son más seguros que claves de larga vida incrustadas en el bundle.
Combina esto con controles básicos del servidor como rate limiting y logging de peticiones para que el abuso aparezca rápido (picos súbitos, fallos repetidos, payloads inusuales).
Mantén el código cliente limitado a identificadores públicos (como un ID de proyecto público) y flags no sensibles (useSandbox: true). Todo lo que conceda acceso o pueda crear cargos pertenece al servidor.
Rotar claves de forma segura con downtime mínimo
Rotar una clave filtrada no es solo “generar una nueva y pegarla”. Si revocas la antigua demasiado pronto, los usuarios reales tendrán errores. Si la mantienes activa demasiado, la filtración puede seguir siendo abusada. Apunta a una superposición controlada y corta.
El enfoque más seguro es la rotación en dos fases: introduce la nueva clave primero, confirma que el tráfico la usa y luego revoca la antigua.
Secuencia de rotación de bajo riesgo
Haz esto cuando puedas vigilar tus logs.
- Crea una clave nueva en el dashboard del proveedor. Nómbrala claramente (por ejemplo:
prod-2026-01-rotation). - Añade la nueva clave a un almacén de secretos servidor-side (no al build del frontend). Mantén la clave antigua activa por ahora.
- Despliega una versión que use la nueva clave.
- Verifica uso en los logs del proveedor y en tus logs de aplicación.
- Revoca la clave antigua después de una breve ventana de superposición (frecuentemente 15 a 60 minutos, más tiempo si los despliegues son lentos o tienes jobs de larga ejecución).
Pon un recordatorio para revocar la clave antigua. Los equipos lo suelen olvidar, especialmente durante un incidente.
No olvides los “llamadores” ocultos
Antes de revocar nada, asegúrate de haber actualizado:
- Jobs en background y tareas programadas
- Webhooks e integraciones de terceros que llamen a tu API
- Apps móviles (pueden tardar en actualizar si los usuarios no actualizan)
- Entornos de staging que compartan accidentalmente claves de producción
- Configuraciones de pruebas locales que aún usen la clave antigua
Después del despliegue, vigila las tasas de error y fallos de autenticación de cerca. Si algo se rompe, revierte rápidamente o extiende la superposición mientras encuentras el llamador perdido.
Errores comunes que mantienen la filtración viva
Si ves un valor en el navegador (View Source, DevTools, requests de red, JS bundle), un atacante también puede verlo.
- La minificación y la ofuscación no protegen secretos. Una clave incrustada en un bundle sigue siendo una clave, aunque esté dividida entre variables o renombrada.
- Tokens de larga duración en
localStorageosessionStorageson fáciles de robar con XSS. Prefiere sesiones de corta duración gestionadas por el servidor (a menudo mediante cookies HTTP-only) y mantén las credenciales reales en el servidor. - Endpoints de depuración y rutas admin se quedan abiertas. Cualquier cosa que omita auth o vuelque datos durante pruebas puede convertirse en una ruta hacia producción.
- El logging de errores del cliente puede filtrar secretos. Evita registrar cabeceras completas de peticiones, tokens o volcados de config en el navegador.
- CORS no es una frontera de seguridad. Solo limita qué navegadores pueden leer respuestas. No impide llamadas directas desde scripts, servidores o herramientas como curl.
Lista rápida antes del despliegue
Úsala justo antes de enviar.
5 comprobaciones que previenen la mayoría de filtraciones
- Confirma que el bundle cliente está limpio: Busca en la salida compilada (no solo en el código fuente) patrones de claves (por ejemplo,
sk_,AIza,Bearer,-----BEGIN, o el nombre de tu proveedor). También confirma que no estás usando prefijos públicos de env (comoNEXT_PUBLIC_oVITE_) para nada que deba mantenerse privado. - Trata los source maps como sensibles: Si mantienes source maps en producción, restringe el acceso. Si no los necesitas, desactívalos en builds de producción.
- Mueve secretos detrás de una frontera servidor: Cualquier llamada que necesite un secreto debe pasar por tu servidor (o función serverless). El navegador solo debe enviar intención de usuario y recibir resultados seguros.
- Bloquea endpoints del servidor: Valida entradas, comprueba auth cuando sea necesario y aplica límites de tasa.
- Rota claves y vigila abusos: Crea nuevas claves primero, despliega el cambio y luego revoca las antiguas. Monitoriza picos de tráfico o costes inusuales.
Un ejemplo simple: si tu frontend llama directamente a un proveedor de mapas o de IA, esa clave será copiada y reutilizada por extraños. En su lugar, el navegador llama a tu endpoint backend, y el backend llama al proveedor con el secreto.
Ejemplo: un prototipo generado por IA que envió una clave real
Un fundador construye un MVP rápido en Lovable. Se ve bien, así que lo despliega el mismo día. La app llama a una API de pago de terceros (como un LLM, mapas, email o analítica), y la clave se establece como variable de entorno. El problema es que la herramienta de build copia ese valor en el bundle del navegador, por lo que la clave acaba dentro del JavaScript enviado.
Un usuario curioso abre DevTools, busca en la pestaña Sources por “key” o el nombre del proveedor y la encuentra en un archivo minificado. La copia y realiza peticiones desde su propio script. En pocas horas, el uso se dispara, los costes suben y el dashboard del proveedor muestra tráfico que no corresponde a usuarios reales.
La solución es simple, pero el orden importa:
- Mueve la llamada API detrás de una ruta servidor (o función serverless) para que el navegador nunca vea el secreto.
- Añade protección básica en esa ruta: comprobaciones de auth, límites de tasa y validación de entrada.
- Rota la clave expuesta y revoca la antigua después de una breve superposición.
- Revisa los logs del proveedor en busca de IPs sospechosas, endpoints inusuales y tráfico fuera de tus horas normales.
- Añade alertas y límites de gasto para que te avisen antes de que los costes se disparen.
Después del cambio, la UI sigue activando la misma funcionalidad. Simplemente llama a tu endpoint backend en lugar del proveedor directamente. El backend lee la clave desde variables de entorno servidor-side y habla con la API de terceros en nombre del usuario. En el navegador no queda nada valioso para robar.
Próximos pasos si tu codebase está desordenada o fue generada por IA
Si tu app fue generada con herramientas como v0, Cursor, Replit, Bolt o Lovable, asume que la misma clave puede copiarse en más de un sitio: un .env, un helper de config, un log “temporal” y el bundle cliente. Por eso las soluciones rápidas fallan. Eliminas una copia, pero otra sigue enviándose.
Céntrate en cada camino que pueda tomar el valor, no solo el obvio. Eso incluye la inyección en tiempo de build, objetos de config empaquetados, funciones serverless y flujos de auth que pasan tokens por el navegador.
Un enfoque práctico cuando el repo es difícil de confiar:
- Identifica los secretos más importantes (pagos, email, base de datos, APIs admin).
- Busca en el repo patrones de clave y nombres de env vars, y luego revisa también la salida compilada.
- Traza cómo se realizan las peticiones: ¿el navegador llama directo al proveedor o pasa por tu servidor?
- Decide la nueva ubicación servidor-side para cada secreto (ruta API, servicio backend, proxy).
- Planifica la rotación de claves después del cambio de código para evitar cortes inesperados.
Si heredaste un prototipo generado por IA y necesitas una intervención rápida enfocada en seguridad, FixMyMess (fixmymess.ai) hace diagnóstico y remediación del codebase para problemas como secretos empaquetados, auth rota y patrones de petición inseguros; ofrecen una auditoría de código gratuita para mapear qué está expuesto antes de empezar a rotar claves.
Preguntas Frecuentes
¿Qué cuenta como “secreto” en una compilación frontend?
Si el navegador necesita el valor para funcionar, es efectivamente público. Todo lo que pueda gastar dinero, leer datos privados, escribir en tu base de datos o firmar/verificar tokens debe tratarse como secreto y mantenerse en el servidor.
¿Protege la minificación u ofuscación las claves API en bundles de JavaScript?
No. La minificación solo cambia el aspecto del código, no su contenido. Cualquiera puede descargar tu bundle y buscar cadenas, así que una clave incrustada en JavaScript sigue estando expuesta.
¿Son seguros de usar para secretos los env vars con prefijos NEXT_PUBLIC_, VITE_ y REACT_APP_?
Esos prefijos están pensados explícitamente para valores que pueden exponerse al navegador. Úsalos solo para ajustes no sensibles, como IDs públicas o flags de UI, y nunca para claves secretas, tokens de servicio o URLs de bases de datos.
¿Cómo puedo decir rápidamente si una clave se está filtrando sin cambiar código?
Abre tu sitio, usa DevTools y revisa las solicitudes de red en busca de query params, cabeceras o cuerpos de petición que contengan tokens. Luego busca en los archivos compilados patrones como “Bearer”, “api_key”, nombres de proveedores o prefijos de clave reconocibles.
¿Por qué empeoran las filtraciones los source maps públicos?
Los source maps no crean la filtración, pero facilitan mucho encontrar y reutilizar secretos porque revelan el código legible y los nombres de variable. Si los mantienes en producción, restringe su acceso o desactívalos para los builds de producción si no los necesitas.
¿Qué debo hacer primero después de encontrar una clave expuesta?
Asume que está comprometida y detén la filtración antes de discutir mucho. Captura dónde la encontraste, identifica qué puede hacer y planifica el cambio para mover el secreto al servidor y rotarlo sin romper producción.
¿Cuál es la forma más segura de mover un secreto fuera del frontend sin romper funcionalidades?
Sustituye la llamada directa del navegador al proveedor por un endpoint en tu backend (o una función serverless) que llame al proveedor usando variables de entorno que solo están en el servidor. El frontend debe enviar la intención del usuario y recibir resultados seguros, no llevar la credencial del proveedor.
¿Está bien almacenar tokens en localStorage o sessionStorage?
Es arriesgado porque cualquier bug de XSS puede robar esos tokens y volver a usarlos. Prefiere sesiones gestionadas por el servidor y credenciales de corta duración cuando sea posible, y evita almacenar secretos de larga vida en el almacenamiento del navegador.
¿Cómo rotar una clave filtrada sin tiempo de inactividad?
Usa un enfoque en dos fases: despliega primero el código que soporta la nueva clave, confirma que el tráfico la está usando, y luego revoca la clave antigua tras una breve superposición. Si la revocas demasiado pronto, los usuarios reales obtienen errores; si esperas demasiado, los atacantes pueden seguir abusando de la clave filtrada.
¿Se filtran más a menudo los prototipos generados por IA, y puede FixMyMess ayudar?
Sí. El código generado por IA a menudo incluye claves de ejemplo en línea, las copia en configuraciones “temporales” o referencia env vars desde el código cliente para que se incluyan en el build. Si el repo no da confianza, FixMyMess puede ejecutar una auditoría de código gratuita, trazar cada ruta de filtración y entregar una remediación enfocada en seguridad—normalmente en 48–72 horas, y en casos urgentes un plan de reconstrucción puede empezar en alrededor de un día.