Un caso con el que se topan a menudo los desarrolladores indie sin formación en seguridad, con todo detalle identificable eliminado y convertido en lección. Aquí no hay pasos para reproducir el ataque. El objetivo es la defensa, para que el mismo error no te ocurra a ti.
- Clase
- Fuga de clave de API / facturación fraudulenta
- Gravedad
- Crítica (un CVSS 10.0 publicado abusado)
- Causa raíz
- Un RCE preautenticación en el framework web dejado sin parchear durante meses
- Alcance de la fuga
- Todos los secretos del entorno
- Alcance de la ejecución
- Quedó dentro de un contenedor sin privilegios (root del host intacto)
- Corrección permanente
- ①actualizar a la versión parcheada ②rotar todas las claves ③monitoreo automático de CVE
«Detuve los cargos» ≠ «resuelto»
Detener la factura es una medida temporal. Cerrar la vía de fuga es una tarea aparte. Solo terminaste cuando hiciste ambas.
Qué pasó (línea de tiempo)
Día 0 — desplegado y en marcha
Una app construida con ayuda de IA se desplegó y corría en producción.Un día — la factura se dispara
La factura de IA en la nube salta de repente. Trabajos masivos en un modelo que jamás se usaría, haciendo tareas que no eran suyas.Investigación — abuso de terceros
«Nuestra app se descontroló» no lo explica. Un tercero la ejecutaba con una clave robada.Rastreo — se investiga la vía de fuga
«¿De dónde se filtró la clave?». Aquí empezó la investigación de verdad.
Por qué no se detectó al principio
En el orden en que se cometieron los errores — porque los errores son la lección.
Decidir la causa demasiado pronto
→ Antes de decidir «culpa mía» o «culpa de ellos», mira primero los datos.
Un grep limpio casi cerró la búsqueda
Ocultar el síntoma pareció una solución
Un grep limpio no es el visto bueno
Aunque no haya ninguna clave en ningún archivo, una vulnerabilidad puede extraer variables de entorno de un proceso en ejecución. Las fugas ocurren en tiempo de ejecución (RCE, cabeceras HTTP), no solo en archivos. Véase qué es un RCE.
La causa real: un CVSS 10.0 publicado y sin parchear
Una firma extraña en registros de acceso antiguos coincidió rápidamente con la inteligencia de amenazas. El rango de versiones del framework web en uso tenía un RCE preautenticación publicado (CVSS 10.0) que ya estaba siendo explotado activamente, y había corrido sin parchear durante meses.
- No era un fallo pasivo — era un atacante ejecutando código en el servidor y exfiltrando el entorno.
- Una cosa limitó el daño: la ejecución quedó dentro de un contenedor sin privilegios (no se tomó el root del host). La revisión de la brecha no encontró puerta trasera persistente, minero ni C2 — el impacto confirmado fue el robo de secretos.
- Como la base de datos interna era alcanzable, la respuesta asumió que el contenido de la BD también se había filtrado.
Lección: cuando algo se comporta de forma extraña, sospecha primero de un CVE conocido. No supongas que es un fallo tuyo.
Acota la fuga: ¿dónde vivía esa clave?
Cuando el grep no encuentra nada, cambia la forma de buscar. Haz la lista de dónde estaba escrita la clave abusada, y las vías de fuga candidatas se reducen. Si la clave abusada solo existió en las variables de entorno del servidor de producción, por ejemplo, sospecha del servidor de producción o del proveedor de hosting, no de Git ni del navegador.
| Dónde se filtra | Causa habitual | Cómo revisar tu propia configuración | Cómo cerrarla |
|---|---|---|---|
| Historial de Git / repos públicos | Hacer commit de .env, claves escritas en el código | Escanea todo el historial con gitleaks git. Revisa las alertas de secret scanning de GitHub | Revoca primero y luego bloquéalo antes del commit |
| JavaScript enviado al navegador | Una clave en una variable NEXT_PUBLIC_, llamar a la API directamente desde el navegador | Busca el prefijo de la clave en la salida del build | Lleva la llamada a la API al código del servidor |
| Una carpeta web pública | .env o copias de seguridad colocadas en la carpeta pública | Abre /.env en tu propio sitio y confirma que devuelve 404 o 403 | Saca los archivos secretos de la carpeta pública |
| Registros / páginas de error | Publicar con la salida de depuración activada, registrar las cabeceras de las peticiones tal cual | Revisa los ajustes de depuración de producción y si Authorization y similares aparecen en los registros | Desactiva la salida de depuración y enmascara los secretos en los registros |
| El servidor en ejecución | Una vulnerabilidad publicada (como un RCE) sin parchear | Compara la versión que realmente se ejecuta con la versión corregida del aviso | Actualiza y después sustituye todos los secretos del entorno |
| El proveedor de hosting | Una brecha del lado del proveedor | Revisa los boletines de seguridad y los avisos por correo del proveedor | Guarda los secretos de forma que los valores no puedan verse después, y rótalos |
| Tu máquina de desarrollo | Un paquete o extensión malicioso | Comprueba si las versiones de paquetes o extensiones que instalaste coinciden con un compromiso publicado | Quita esa versión y sustituye los secretos que había en la máquina |
Este caso fue la fila del «servidor en ejecución». Las cuatro filas de abajo son vías que no dejan rastro en tu código ni en Git. La Secrets Management Cheat Sheet de OWASP también advierte de que las variables de entorno suelen ser legibles por otros procesos del mismo entorno y pueden acabar en registros o volcados. Así que cuando se abusa de una clave, trata como filtrados todos los secretos guardados en el mismo sitio.
Las claves separadas aceleran la investigación
Si usas una clave distinta por servicio y por entorno (producción frente a desarrollo), la clave abusada por sí sola te dice en qué fila de la tabla estás. Si reutilizas una clave en todas partes, tienes que sospechar de todos los sitios. Cómo separar las claves se explica en cuando una clave de API robada hace que bloqueen tu cuenta de OpenAI.
Cuenta las vulnerabilidades según la versión que realmente se ejecuta
Al revisar las demás dependencias, se reportaron «8 dependencias vulnerables más». También era incorrecto. Se habían contado por el límite inferior en package.json (el rango con el caret ^). Las verdaderamente peligrosas eran solo las 2 dependencias fijadas que habían quedado atrás.
Comprueba tus propias versiones en ejecución
Mira las versiones realmente resueltas, en el lockfile o en el contenedor en ejecución.
# npm: see what's actually installed
npm ls next react react-dom
# inside a running container
docker exec <container> npm ls <package>Los números de aquí son las versiones que realmente se ejecutan, no el ^ de package.json.
Cómo revisar tu propia configuración
Tanto si acabas de ver una factura extraña como si todavía no ha pasado nada, puedes revisarlo en este orden. Cada paso solo mira tu propia cuenta y tu propio servidor.
Mira qué clave se usó, y cuándo
Revisa los archivos enviados al navegador
NEXT_PUBLIC_ se insertan en el momento del build en el JavaScript que se envía al navegador. Tras un build, busca el prefijo de tu clave en la salida, por ejemplo grep -rl "sk-" .next/static. Cualquier cosa que aparezca ahí la podía leer cualquiera.Confirma que los archivos secretos no son accesibles desde la carpeta pública
curl -sI https://example.com/.env contra tu propio sitio (sustituye example.com por tu dominio) y confirma que el estado de la primera línea no es 200 (debería ser 404 o 403). Más en auditar archivos secretos en directorios públicos.Compara la versión de producción con el aviso
npm ls dentro del contenedor de producción para ver la versión. osv-scanner scan -L pnpm-lock.yaml comprueba si tu lockfile incluye versiones con vulnerabilidades conocidas.Busca secretos en los registros
gitleaks dir --redact sobre la carpeta donde se guardan los registros de tu app para encontrar claves escritas en ellos. --redact evita que los valores encontrados aparezcan en pantalla.Revisa límites y alertas
Lo que se hizo primero (pasos reproducibles)
Revocar de inmediato la clave abusada
Actualizar el framework a la versión parcheada
Rotar todos los secretos
Revisión de la brecha
Defensa del lado de la cuenta
Prevención: monitorea los CVE de forma automática
Reducido a lo esencial, este incidente fue «un humano pasó por alto un CVSS 10.0 publicado». Las revisiones manuales siempre se pierden cosas. La monitorización automática las detecta.
Escaneo de dependencias que puedes empezar hoy
Gratis para empezar — un paso en CI.
# OSV scanner (Google) checks your lockfile
osv-scanner scan -L pnpm-lock.yaml
# On GitHub, enable Dependabot (repo Settings → Security)Este sitio mismo, conforme a esta lección, monitorea sus propias dependencias en busca de CVE — practicamos lo que recomendamos.
Errores habituales y cómo corregirlos
- Poner una clave nueva en un servidor que sigue abierto: la misma vía roba también la clave nueva. Detén enseguida la clave abusada, corrige la vulnerabilidad y después termina de sustituir el resto de secretos.
- Tratar una alerta de presupuesto como un tope: Google Cloud indica que un presupuesto que solo tiene alertas no detiene automáticamente el uso ni el gasto. Si quieres que se detenga, añade un límite que bloquee el uso, como el límite estricto mensual de OpenAI o un límite de gasto de Workspace en Anthropic.
- Poner una clave de API de IA en un navegador o en una app móvil: cualquier valor que llegue al dispositivo del usuario puede extraerse. Llama a la API de IA desde tu servidor, y que el navegador llame a tu servidor.
- Borrarla de los archivos o del historial de Git y quedarse ahí: quien ya la copió puede seguir usándola. Revoca primero y limpia el historial después (ver por qué las credenciales filtradas en GitHub siguen siendo válidas, en inglés).
- Usar la misma clave en desarrollo y en producción: no puedes saber qué lado se filtró, así que tienes que detener ambos. Emite una clave distinta por entorno.
Un ejemplo público: variables de entorno filtradas desde el lado del proveedor
Las fugas que ninguna búsqueda en archivos puede encontrar también ocurren fuera de tu propio servidor. El 19 de abril de 2026, Vercel comunicó un acceso no autorizado a sus sistemas internos. Según la empresa, el compromiso de una herramienta de IA de un tercero usada por un empleado llevó al robo de variables de entorno de algunos clientes que no estaban marcadas como «sensitive» (el ajuste que hace que un valor no pueda verse después de guardarlo).
Vercel recomendó a los clientes revisar y rotar las variables de entorno no marcadas como sensitive, y revisar los registros de actividad y los despliegues recientes. También indicó que borrar los proyectos o la cuenta no basta para eliminar el riesgo.
Los clientes en esa situación podían buscar en su código y en Git todo el día sin encontrar rastro de una fuga. La acción es la misma que en este caso: sustituir todos los secretos guardados en ese sitio. Los pasos están en el incidente de seguridad de Vercel (abril de 2026) (en inglés).
Lecciones
Errores comunes
- Dar por terminado al «detener los cargos»
- Relajarse porque el grep está limpio
- Enmascarar el síntoma en un proxy y sentirlo resuelto
- Contar vulnerabilidades por el piso de
package.json - Rotar solo la única clave de la que se confirmó abuso
Defensa correcta
- Tratar detener los cargos y cerrar la vía de fuga como dos trabajos distintos, y hacer ambos
- Sospechar también de fugas en tiempo de ejecución (RCE, cabeceras)
- Actualizar el framework que lo causó
- Juzgar por la versión realmente en ejecución
- Reemplazar todo el entorno si se filtra
En resumen: el pico de la factura fue un síntoma, y la causa fue un RCE CVSS 10.0 sin parchear. La causa correcta no se encontró al primer intento; se encontró equivocándose varias veces y volviendo a comprobar.
Leer a continuación
- Tras una fuga: 543.699 credenciales expuestas en GitHub siguen siendo válidas — por qué hay que revocar, no solo borrar (en inglés)
- Glosario: qué es un RCE · qué es un CVE · qué es CVSS
- Defensa: ejecutar Next.js de forma segura (higiene de CVE)
- Incidente: cuando un .env quedó expuesto al mundo entero
- Fundamentos: fundamentos de .env y claves de API (qué hacer tras una fuga)
- Prevención: detener los secretos antes del commit con gitleaks
- Incidente: cuando una clave de API robada hace que bloqueen tu cuenta de OpenAI
Fuentes
- OWASP, "Secrets Management Cheat Sheet" (riesgos de las variables de entorno, respuesta ante fugas): cheatsheetseries.owasp.org
- Documentación de Next.js, "How to use environment variables in Next.js" (inserción de
NEXT_PUBLIC_): nextjs.org - OSV-Scanner, "Usage": google.github.io
- OpenAI, "Production best practices" (manejo de claves, alertas y límites): developers.openai.com
- Centro de ayuda de Claude, "Creating and managing Workspaces" (límites y claves de Workspace): support.claude.com
- Google Cloud, "Create, edit, or delete budgets and budget alerts": docs.cloud.google.com
- Vercel, "Vercel April 2026 security incident": vercel.com