Serie Vibe Coding Security
- ¿Qué es la Seguridad del Vibe Coding? Una 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
- Escaneando Aplicaciones Vibe-Coded: Por Qué el SAST/DAST Tradicional Se Queda Corto
- Prompt Engineering para Código Seguro
- El Checklist de Seguridad del Fundador (estás aquí)
- Asegurando el Pipeline de Programación con IA (próximamente)
- El Futuro de la Seguridad del Vibe Coding (próximamente)
Tiempo de lectura: 18 minutos
TL;DR
Has construido tu MVP con IA. Funciona, los usuarios se están registrando y estás pensando en el lanzamiento. Antes de hacerlo, recorre estas quince comprobaciones. Cubren las vulnerabilidades que veo con más frecuencia en aplicaciones vibe-coded — las que llevan a brechas de datos, credenciales filtradas y correos de «tenemos que cerrar todo» enviados a tus usuarios. Cada comprobación tiene un test que puedes ejecutar en menos de cinco minutos, la mayoría desde un navegador o un solo comando de terminal. Imprime el resumen del final y pégalo al lado de tu monitor.
Por Qué Existe Este Checklist
Un fundador con el que trabajé lanzó su MVP vibe-coded un jueves. Para el sábado por la noche le habían volcado la base de datos — cada email de usuario, cada registro, todo. Un atacante encontró el puerto de MongoDB expuesto, se conectó sin credenciales y exfiltró el contenido completo. El fundador había fallado en tres elementos de la lista que estás a punto de leer. Le llevó diez minutos ejecutar las comprobaciones después de la brecha. Le habría llevado diez minutos antes.
Construí la primera versión de este checklist en VULNEX tras presentar en una conferencia de seguridad en 2025, basándome en las vulnerabilidades que no dejaba de ver en código generado por IA. Desde entonces, el patrón no ha hecho más que empeorar. El informe de GitGuardian de 2026 encontró 28,65 millones de nuevos secretos filtrados en GitHub en 2025 — un aumento del 34% interanual. Los commits que involucran asistentes de programación con IA filtran secretos a más del doble de la tasa base. La investigación de Apiiro mostró que el código de IA añadía más de 10.000 nuevos hallazgos de seguridad al mes en los repositorios estudiados a mediados de 2025. Las brechas que cubrí en la Parte 3 — Moltbook, Enrichlead, aplicaciones comprometidas días después de su lanzamiento — todas fallaron en elementos de esta lista.
Esto no es un programa de seguridad completo. Son las quince cosas que, si las haces mal, garantizan que alguien encuentre el agujero antes que tú. Si las haces bien, estarás por delante de la gran mayoría de MVPs vibe-coded que se lanzan hoy.
Las comprobaciones están agrupadas en cinco áreas. Usaré QuickNote — la aplicación de notas deliberadamente vulnerable del resto de esta serie — y algunos otros ejemplos reales para hacer cada una concreta.
Área 1: El Perímetro
Lo que los atacantes ven en el momento en que apuntan un navegador o un escáner de puertos a tu aplicación.
Comprobación 1: Forzar HTTPS en todas las páginas
Las configuraciones de despliegue generadas por IA rutinariamente omiten HTTPS. El modelo te da una aplicación Node.js funcional escuchando en el puerto 3000 por HTTP plano — lo cual está bien para desarrollo local y es catastrófico en producción. Sin HTTPS, cada inicio de sesión, cada token de API, cada dato de usuario viaja por internet en texto plano. Cualquiera en la misma red — una cafetería, una oficina compartida, un ISP comprometido — puede leerlo.
Cómo comprobarlo:
curl -I http://tuapp.com
Quieres una redirección 301 o 308 a https://. Si obtienes un 200 en HTTP plano, tu aplicación está sirviendo contenido sin cifrar. Comprueba también que tu API responda solo por HTTPS — curl -I http://tuapp.com/api/notes debería redirigir, no devolver datos.
Cómo solucionarlo: Si estás en Vercel, Netlify o Cloudflare Pages, HTTPS se fuerza automáticamente. En un VPS o despliegue Docker, configura tu proxy inverso (Nginx, Caddy) para redirigir todo el HTTP a HTTPS. Caddy lo hace por defecto — una razón por la que lo recomiendo para fundadores que no quieren complicarse con certificados TLS.
Comprobación 2: Configurar cabeceras de seguridad
Abre securityheaders.com y escanea tu dominio. Si obtienes algo por debajo de una B, tienes trabajo por hacer. En toda la web, solo el 21,9% de los sitios despliegan una Content Security Policy — y las aplicaciones vibe-coded están muy por debajo de esa media porque la IA raramente genera configuración de cabeceras de seguridad a menos que lo pidas.
Cómo comprobarlo:
curl -I https://tuapp.com | grep -iE "strict-transport|content-security|x-frame|x-content-type"
Quieres ver al menos estas cuatro cabeceras en la respuesta: Strict-Transport-Security, Content-Security-Policy, X-Frame-Options y X-Content-Type-Options. Si no ves ninguna, tu aplicación tiene cero protección contra clickjacking, sniffing de MIME y ataques de degradación de protocolo.
Cómo solucionarlo: Añádelas en tu proxy inverso, tu middleware de Express o la configuración de tu plataforma de hosting. Un conjunto razonable para un MVP:
Strict-Transport-Security: max-age=31536000; includeSubDomains
Content-Security-Policy: default-src 'self'; script-src 'self'
X-Frame-Options: DENY
X-Content-Type-Options: nosniff
Referrer-Policy: strict-origin-when-cross-origin
Permissions-Policy: camera=(), microphone=(), geolocation=()
Ajusta Content-Security-Policy según lo que tu aplicación realmente carga — si usas un CDN para scripts, añade su dominio a script-src. Si tu aplicación deja de funcionar después de añadir CSP (habitual con aplicaciones React que usan scripts inline), empieza con script-src 'self' 'unsafe-inline' y refuerza después. Una CSP imperfecta es mejor que ninguna CSP.
Comprobación 3: Cerrar puertos expuestos y paneles de administración
Las guías de despliegue de IA a menudo dejan puertos de base de datos abiertos a internet. A principios de 2026, Shodan indexa más de 213.000 instancias de MongoDB expuestas — muchas sin autenticación requerida. Si usas Firebase, no asumas que estás a salvo: RedHunt Labs descubrió que 1 de cada 5 bases de datos de Firebase tenían reglas mal configuradas que permitían acceso público de lectura, exponiendo emails, contraseñas y mensajes privados. Tu base de datos nunca debería ser accesible desde internet — y «gestionada» no significa «asegurada.»
Cómo comprobarlo:
nmap -Pn -p 5432,27017,6379,3306,9200 tuapp.com
Eso escanea PostgreSQL (5432), MongoDB (27017), Redis (6379), MySQL (3306) y Elasticsearch (9200). Todos esos puertos deberían mostrar filtered o closed. Si alguno muestra open, tu base de datos es directamente accesible desde internet — y si usa credenciales por defecto o no tiene autenticación (como suele pasar con Redis), probablemente ya está comprometida.
Comprueba también los paneles de administración: navega a /admin, /dashboard, /supabase, /_next, /graphql, /phpmyadmin. Si alguno carga sin requerir autenticación desde internet, bloquéalo o elimínalo.
Cómo solucionarlo: Configura el firewall de tu proveedor de hosting para permitir conexiones a la base de datos solo desde la IP de tu servidor de aplicaciones. En AWS, eso es una regla de security group. En un VPS, usa ufw allow from <ip-app> to any port 5432. Para paneles de administración, ponlos detrás de autenticación o restringe el acceso por IP.
Área 2: Secretos
La categoría más común de vulnerabilidad en vibe coding. La IA genera código con secretos embebidos porque eso es lo que muestran los datos de entrenamiento — el código de tutoriales hardcodea credenciales por simplicidad, y el modelo reproduce el patrón.
Comprobación 4: Escanear tu código en busca de secretos hardcodeados
De los 28,65 millones de secretos filtrados en GitHub en 2025, una parte desproporcionada vino de código generado por IA. GitGuardian encontró que los commits que involucran un asistente de programación con IA filtraban secretos a una tasa del 3,2% — más del doble de la tasa base del 1,5% en GitHub público. El modelo pone tu clave de servicio de Supabase en una constante, tu clave secreta de Stripe en un objeto de configuración, tu cadena de conexión de base de datos en un archivo Docker Compose. Lo hace porque es lo que funciona, y el código funcional es para lo que optimiza. Imagina esto: un fundador sube una clave secreta de Stripe a un repositorio público a las 14:00. A las 16:00, los bots ya la han encontrado. A las 18:00, los cargos fraudulentos empiezan a llegar a su cuenta. Esto pasa todos los días — los datos de GitGuardian muestran que los secretos filtrados se explotan típicamente en cuestión de horas.
Cómo comprobarlo:
# Instalar y ejecutar Gitleaks en tu repositorio
gitleaks detect --source . --report-format json --report-path leaks.json
O usa TruffleHog para un escaneo más profundo incluyendo el historial de git:
trufflehog git file://. --json
Cualquier hallazgo son secretos que se han subido a tu repositorio. Aunque los elimines del código actual, están en tu historial de git — y si el repositorio fue público en algún momento, ya han sido extraídos.
Cómo solucionarlo: Rota cada secreto filtrado inmediatamente — no te limites a eliminarlo del código. Mueve todos los secretos a variables de entorno cargadas en tiempo de ejecución. Si estás en Vercel, Railway o Render, usa su interfaz de variables de entorno. Nunca pongas secretos en archivos .env que se suban a git. Lo que nos lleva a la siguiente comprobación.
Comprobación 5: Verificar que los archivos .env y las imágenes Docker no filtran secretos
Dos canales ocultos que la IA crea rutinariamente para la filtración de secretos. Primero: archivos .env. El modelo crea un .env con tus credenciales de base de datos pero no siempre lo añade a .gitignore. Segundo: imágenes Docker. Como cubrí en la Parte 5, los Dockerfiles generados por IA a menudo insertan secretos en la build con instrucciones ARG y ENV, haciéndolos visibles en el historial de capas de la imagen.
Cómo comprobarlo:
# Comprobar si .env está en tu gitignore
grep "\.env" .gitignore
# Comprobar si algún archivo .env está siendo rastreado por git
git ls-files | grep -i "\.env"
# Comprobar la imagen Docker en busca de secretos filtrados
docker history --no-trunc tuapp:latest | grep -iE "key|secret|password|token"
Si git ls-files muestra algún archivo .env, ese archivo — y cada secreto en él — está en el historial de tu repositorio. Si docker history muestra credenciales, cualquiera que descargue tu imagen puede extraerlas.
Cómo solucionarlo: Añade .env* a .gitignore antes de tu primer commit. Para Docker, usa builds multi-stage y pasa los secretos como variables de entorno en tiempo de ejecución, nunca como argumentos de build. Si ya hay secretos en el historial de git, necesitas usar git filter-repo para purgarlos — y rotar cada secreto expuesto.
Comprobación 6: Bloquear CORS
Los errores de configuración de Cross-Origin Resource Sharing están por todas partes en las aplicaciones vibe-coded. Los problemas de CORS se sitúan de forma consistente entre las vulnerabilidades de aplicaciones web más comunes, y las aplicaciones vibe-coded son especialmente propensas porque la configuración típica de Express.js generada por IA incluye cors() sin argumentos — lo que por defecto usa Access-Control-Allow-Origin: *, permitiendo que cualquier sitio web en internet haga peticiones autenticadas a tu API.
Cómo comprobarlo:
curl -H "Origin: https://evil.com" -I https://tuapp.com/api/notes
Mira la cabecera Access-Control-Allow-Origin en la respuesta. Si dice * o refleja https://evil.com, tu API servirá datos felizmente a cualquier sitio web que pregunte — incluyendo una página de phishing de un atacante.
Cómo solucionarlo: Configura CORS para permitir solo tus propios dominios:
app.use(cors({
origin: ['https://tuapp.com', 'https://www.tuapp.com'],
credentials: true
}));
Nunca uses origin: true (refleja cualquier origen) ni dejes CORS con el wildcard por defecto en producción.
Área 3: Autenticación y Acceso
Aquí es donde las aplicaciones vibe-coded fallan más estrepitosamente. La IA construye autenticación que funciona — puedes iniciar sesión, ves tus datos — pero se salta los controles que impiden que todos los demás también vean tus datos. Cubrí los detalles en la Parte 5, pero aquí tienes cómo comprobar los fallos críticos.
Comprobación 7: Añadir rate limiting al login y registro
Sin rate limiting, tu endpoint de login acepta intentos de contraseña ilimitados. El credential stuffing — ataques automatizados usando pares de usuario/contraseña filtrados de otras brechas — genera 26.000 millones de intentos al mes a nivel global. Microsoft Entra bloquea 7.000 ataques de contraseña por segundo. Si tu login no tiene rate limit, un atacante puede probar miles de contraseñas por minuto contra las cuentas de tus usuarios.
QuickNote tenía exactamente esta vulnerabilidad. Sin rate limiter en /api/login, un atacante podía hacer fuerza bruta sobre la contraseña de cualquier cuenta a la velocidad de su conexión a internet.
Cómo comprobarlo:
# Enviar 20 peticiones rápidas a tu endpoint de login
for i in $(seq 1 20); do
curl -s -o /dev/null -w "%{http_code}\n" \
-X POST https://tuapp.com/api/login \
-H "Content-Type: application/json" \
-d '{"email":"test@test.com","password":"wrong"}';
done
Si las 20 devuelven 401 (credenciales inválidas) sin ningún 429 (demasiadas peticiones), no tienes rate limiting. Deberías empezar a ver respuestas 429 después de 5-10 intentos.
Cómo solucionarlo: En Express.js, añade express-rate-limit:
const loginLimiter = rateLimit({
windowMs: 60 * 1000,
max: 5,
message: { error: 'Too many attempts, try again later' }
});
app.post('/api/login', loginLimiter, loginHandler);
Aplica rate limiting a los endpoints de registro y recuperación de contraseña también — se atacan con la misma frecuencia.
Comprobación 8: Verificar que cada endpoint de API comprueba la autenticación
Las APIs generadas por IA a menudo tienen autenticación en algunos endpoints pero no en otros. El modelo construye un flujo de login, genera un token, y luego se olvida de comprobar ese token en la mitad de las rutas. He revisado aplicaciones vibe-coded donde /api/login estaba correctamente protegido pero /api/users, /api/notes y /api/admin aceptaban peticiones sin autenticar.
Cómo comprobarlo:
# Intenta acceder a tus endpoints de API sin token de autenticación
curl -s https://tuapp.com/api/notes
curl -s https://tuapp.com/api/users
curl -s https://tuapp.com/api/settings
Cada endpoint protegido debería devolver 401 Unauthorized cuando se llama sin un token válido. Si alguno devuelve datos, ese endpoint es públicamente accesible para cualquiera que conozca la URL.
Cómo solucionarlo: Añade middleware de autenticación que se ejecute en cada ruta por defecto, y luego exime explícitamente solo las rutas públicas (login, registro, health check). En Express.js:
// Eximir rutas públicas ANTES del middleware de auth
app.post('/api/login', loginHandler);
app.post('/api/signup', signupHandler);
// Luego aplicar middleware de auth a todo lo demás bajo /api
app.use('/api', authMiddleware);
Comprobación 9: Comprobar que los usuarios solo acceden a sus propios datos
Esta es la vulnerabilidad IDOR — Insecure Direct Object Reference — y es el fallo más peligroso en aplicaciones vibe-coded multiusuario. La aplicación funciona correctamente cuando la usas normalmente: ves tus notas, tus facturas, tu perfil. Pero si cambias el ID en la URL o en la petición a la API, ves los datos de otra persona. QuickNote tenía esto: cambiar /api/notes/42 por /api/notes/43 devolvía las notas privadas de otro usuario. Sin comprobación de propiedad, sin autorización — solo una consulta a la base de datos por ID.
Cómo comprobarlo:
# Inicia sesión como usuario A, obtén su token, y anota el ID de un recurso que le pertenece
# Luego intenta acceder a un recurso que pertenece al usuario B
curl -H "Authorization: Bearer <token-usuario-a>" \
https://tuapp.com/api/notes/9999
Si esto devuelve datos (en vez de 403 Forbidden), cualquier usuario autenticado puede acceder a los datos de cualquier otro usuario adivinando o incrementando IDs. Si tu aplicación usa IDs enteros auto-incrementales, un atacante puede enumerar cada registro de tu base de datos.
Cómo solucionarlo: Añade una cláusula WHERE user_id = authenticated_user_id a cada consulta de base de datos. Si estás en Supabase, habilita Row Level Security y crea políticas:
CREATE POLICY notes_owner ON notes
USING (user_id = auth.uid());
Prueba la política iniciando sesión como dos usuarios diferentes y verificando que ninguno puede ver los datos del otro.
Área 4: Manejo de Datos
Cómo tu aplicación procesa lo que los usuarios le envían. El código generado por IA es optimista por defecto — asume que toda la entrada está bien formada y es de confianza. Los atacantes no envían entradas bien formadas.
Comprobación 10: Validar toda la entrada en el servidor
Si tu aplicación tiene un formulario, prueba qué pasa cuando pones <script>alert('xss')</script> en cada campo de texto. Si tu aplicación tiene búsqueda, prueba '; DROP TABLE users; --. El código generado por IA casi nunca valida la entrada en el servidor a menos que lo pidas específicamente. La validación del lado del cliente (atributos HTML required, comprobaciones JavaScript) se elude trivialmente — abre las herramientas de desarrollo del navegador y elimina la validación, o envía peticiones directamente con curl.
Imagina que has construido una aplicación de facturación para freelancers con IA. El campo «nombre de empresa» en el formulario de factura probablemente acepta cualquier cadena. Un atacante pone una etiqueta de script en el nombre de empresa, genera una factura, y cuando tu cliente abre esa factura en PDF o vista web — el script se ejecuta en su navegador, potencialmente robando su sesión.
Cómo comprobarlo:
# Probar XSS en un campo de texto
curl -X POST https://tuapp.com/api/notes \
-H "Authorization: Bearer
<token>" \
-H "Content-Type: application/json" \
-d '{"title":"<script>alert(1)</script>","content":"test"}'
# Probar inyección SQL en un parámetro de búsqueda
curl "https://tuapp.com/api/search?q=test%27%20OR%201=1--"
Si la etiqueta de script se almacena y se renderiza de vuelta sin escapar, tienes XSS almacenado. Si la prueba de inyección SQL devuelve más datos de los esperados, tienes inyección SQL.
Cómo solucionarlo: Valida y sanitiza toda la entrada en el servidor. Usa una librería de validación como Zod o Joi en Node.js. Define qué debe aceptar cada campo — tipo de dato, longitud máxima, conjunto de caracteres — y rechaza cualquier cosa que no coincida. Sanitiza HTML con una librería como DOMPurify antes de renderizar contenido generado por usuarios.
Comprobación 11: Usar consultas parametrizadas
Esta es la defensa del servidor contra la inyección SQL. Las consultas con concatenación de cadenas — donde la entrada del usuario se pega directamente en la cadena SQL — son una de las vulnerabilidades más antiguas y peligrosas del desarrollo web. La IA las genera regularmente porque los datos de entrenamiento están llenos de ellas.
Cómo comprobarlo:
# Buscar concatenación de cadenas en SQL en tu código
grep -rn "query.*\`.*\${" ./src/
grep -rn "query.*+.*req\." ./src/
grep -rn "f\".*SELECT" ./src/
Cualquier coincidencia es una potencial vulnerabilidad de inyección SQL. El patrón query(\SELECT FROM notes WHERE id = ${noteId}`)es vulnerable. El patrónquery(‘SELECT FROM notes WHERE id = $1′, [noteId])` es seguro.
Cómo solucionarlo: Reemplaza cada consulta con concatenación de cadenas por consultas parametrizadas. En Node.js con pg:
// Vulnerable
db.query(`SELECT * FROM notes WHERE id = ${noteId}`);
// Seguro
db.query('SELECT * FROM notes WHERE id = $1', [noteId]);
Si usas un ORM como Prisma o Drizzle, estás generalmente a salvo por defecto — pero revisa si hay llamadas a $queryRawUnsafe o $executeRawUnsafe, que puentean las protecciones del ORM.
Comprobación 12: No almacenar tokens ni datos sensibles en localStorage
Esta es la vulnerabilidad que le da a un atacante control total de la cuenta a través de cualquier agujero XSS. localStorage es accesible por cada script que se ejecuta en tu página. Si un atacante encuentra cualquier forma de inyectar JavaScript — mediante un XSS almacenado en un campo de perfil de usuario, mediante un script de terceros comprometido, mediante una extensión del navegador — puede leer cada token en localStorage y enviarlo a su servidor.
QuickNote almacenaba tokens de acceso JWT en localStorage. Combinado con la falta de validación de entrada, esto significaba que cualquier vulnerabilidad XSS le daba a un atacante el token de autenticación de cada usuario.
Cómo comprobarlo:
Abre tu aplicación en el navegador, inicia sesión, y luego abre las Herramientas de Desarrollo (F12) → Aplicación → Local Storage. Si ves algo etiquetado como token, access_token, jwt, session o similar — eso es un hallazgo. Comprueba también sessionStorage.
Cómo solucionarlo: Almacena los tokens de autenticación en cookies httpOnly con los flags Secure y SameSite=Strict. Estas cookies son invisibles para JavaScript — el XSS no puede leerlas, y se envían automáticamente con cada petición a tu servidor. Esto es lo que produce por defecto el prompt orientado a seguridad de la Parte 7.
Área 5: Dependencias y Despliegue
Lo que lanzaste junto con tu propio código. Las herramientas de IA incorporan dependencias que nunca elegiste, generan configuraciones que nunca revisaste, y crean manejo de errores que le dice a los atacantes exactamente qué salió mal.
Comprobación 13: Auditar tus dependencias en busca de vulnerabilidades conocidas
Cada dependencia que tu herramienta de IA añadió es una superficie de ataque que no aceptaste conscientemente. El informe de Sonatype de 2026 documentó 454.648 nuevos paquetes maliciosos en 2025 — un aumento del 75% interanual. Tu asistente de programación con IA eligió paquetes basándose en la popularidad en los datos de entrenamiento, no en si han sido parcheados recientemente o si han sido marcados como maliciosos.
Cómo comprobarlo:
# Node.js
npm audit
# Python
pip-audit
# O usa Snyk para un informe más detallado
npx snyk test
npm audit viene integrado en Node.js y se ejecuta en segundos. Presta atención a los hallazgos de severidad high y critical. pip-audit hace lo mismo para Python. Para un análisis más profundo incluyendo dependencias transitivas y alcanzabilidad, Snyk y Endor Labs ofrecen planes gratuitos.
Cómo solucionarlo: Ejecuta npm audit fix para parches automáticos. Para vulnerabilidades que no se pueden corregir automáticamente, comprueba si una versión más reciente del paquete las resuelve, o busca un paquete alternativo. Cubrí el flujo completo de gestión de dependencias en la Parte 4.
Comprobación 14: Restringir la subida de archivos
Si tu aplicación acepta subida de archivos — fotos de perfil, documentos, adjuntos — prueba qué pasa cuando subes algo que no es lo que el formulario espera. La subida de archivos sin restricción es una clase de vulnerabilidad con CVSS 10.0. En abril de 2025, CVE-2025-31324 — una subida de archivos sin autenticación en SAP NetWeaver — fue explotada activamente para subir webshells y lograr ejecución remota de código completa. El mismo patrón aparece en aplicaciones vibe-coded: la IA genera un endpoint de subida que guarda lo que sea que recibe en el sistema de archivos, sin comprobación de tipo, sin límite de tamaño, sin sanitización del nombre de archivo.
Cómo comprobarlo: Intenta subir un archivo con extensión .html o .svg a través del formulario de subida de tu aplicación. Si se guarda y es accesible en una URL pública, intenta acceder a él en un navegador — si el HTML se renderiza o el SVG ejecuta JavaScript, tienes XSS almacenado vía subida de archivos. Prueba también a subir un archivo muy grande (100MB+) — si no hay límite de tamaño, eso es un vector de denegación de servicio.
Cómo solucionarlo: Valida el tipo de archivo en el servidor comprobando los magic bytes del archivo, no solo la extensión (las extensiones se pueden falsificar). Limita el tamaño del archivo. Almacena las subidas en un bucket de almacenamiento dedicado (S3, Cloudflare R2) con una sobreescritura de content-type que fuerce la descarga en vez de renderizar. Nunca sirvas archivos subidos por usuarios desde el mismo dominio que tu aplicación — usa un subdominio o dominio de CDN separado.
Comprobación 15: Asegurarse de que los errores no filtran detalles internos
El código generado por IA deja mensajes de error detallados en producción. Trazas de pila, cadenas de conexión de base de datos, rutas de archivos, versiones de paquetes — toda información que ayuda a un atacante a entender tu infraestructura y encontrar su siguiente exploit. El manejador de errores por defecto de Express.js, por ejemplo, envía la traza de pila completa al cliente en modo desarrollo — y el código generado por IA a menudo no cambia al modo de producción en el despliegue.
Cómo comprobarlo:
# Provocar un error solicitando un recurso que no existe
curl https://tuapp.com/api/notes/id-inexistente-999999
# Intentar enviar datos malformados
curl -X POST https://tuapp.com/api/notes \
-H "Content-Type: application/json" \
-d '{"json invalido'
Si la respuesta incluye una traza de pila, rutas de archivos (como /app/src/routes/notes.js:42), errores de base de datos (como relation "users" does not exist), o números de versión de frameworks — tu manejo de errores está filtrando información.
Cómo solucionarlo: Establece NODE_ENV=production en tu entorno de despliegue. Añade un manejador de errores global que capture todos los errores y devuelva un mensaje genérico al cliente mientras registra los detalles en el servidor:
app.use((err, req, res, next) => {
console.error(err); // Registrado en el servidor, no enviado al cliente
res.status(500).json({ error: 'Internal server error' });
});
El Checklist Imprimible
Imprímelo. Pégalo al lado de tu monitor. Recórrelo antes de cada despliegue. Descarga la versión PDF de una página si prefieres una impresión más limpia.
El Perímetro
- 1. HTTPS forzado en todas las páginas —
curl -I http://tuapp.comdevuelve redirección 301/308 - 2. Cabeceras de seguridad configuradas — puntuación B o superior en securityheaders.com
- 3. Sin puertos de BD ni paneles de admin expuestos —
nmap -p 5432,27017,6379muestra filtered/closed
Secretos
- 4. Sin secretos hardcodeados —
gitleaks detectdevuelve cero hallazgos - 5. .env excluido de git, sin secretos en capas Docker —
git ls-files | grep .envno devuelve nada - 6. CORS bloqueado a tus dominios —
curl -H "Origin: https://evil.com"no refleja el origen
Autenticación y Acceso
- 7. Rate limiting en login/registro — 20 peticiones rápidas provocan respuestas 429
- 8. Cada endpoint de API requiere autenticación — curl sin autenticar devuelve 401
- 9. Los usuarios solo acceden a sus propios datos — prueba de ID cruzado devuelve 403
Manejo de Datos
- 10. Validación de entrada en el servidor — etiquetas
<script>rechazadas o escapadas - 11. Consultas parametrizadas —
grepno encuentra SQL con concatenación de cadenas - 12. Sin tokens en localStorage — las herramientas de desarrollo no muestran tokens de auth en storage
Dependencias y Despliegue
- 13. Dependencias auditadas —
npm auditmuestra cero hallazgos high/critical - 14. Subida de archivos restringida — tipo, tamaño y ubicación de almacenamiento validados
- 15. Los errores no filtran detalles — peticiones malformadas devuelven mensajes genéricos, sin trazas de pila
Si solo puedes arreglar tres cosas hoy
Si has ejecutado el checklist y has fallado en varios puntos, aquí tienes por dónde empezar:
Primero: Comprobación 4 (secretos hardcodeados). Si Gitleaks ha encontrado secretos en tu repositorio, ya están filtrados. Cada minuto que esperas es un minuto que un atacante puede usar esas credenciales. Rótalos ahora — antes de arreglar cualquier otra cosa.
Segundo: Comprobación 9 (usuarios accediendo a datos de otros usuarios). Si tu prueba de IDOR ha fallado, cualquier usuario autenticado puede navegar por toda tu base de datos incrementando IDs. Esta es la vulnerabilidad que convierte un incidente de seguridad en una notificación de brecha de datos.
Tercero: Comprobación 1 (HTTPS). Sin HTTPS, cada corrección que apliques después puede ser interceptada en tránsito. HTTPS es la base — nada más funciona sin él.
Todo lo demás importa, pero estos tres son los puntos donde la distancia entre «vulnerable» y «comprometido» se mide en horas, no en semanas.
Lo Que Este Checklist No Cubre
Quince puntos no pueden cubrirlo todo. Este checklist es el suelo, no el techo. Algunas cosas que necesitarás más allá de esta lista cuando crezcas más allá del MVP:
Test de penetración. Una vez que tengas usuarios de pago, contrata a un profesional para que intente entrar. En VULNEX hacemos este tipo de trabajo regularmente, y puedo decirte que un pentest casi siempre encuentra cosas que ningún checklist detecta — fallos de lógica de negocio, condiciones de carrera, problemas en los límites de confianza que solo salen a la luz cuando un humano piensa como un atacante contra tu aplicación específica.
Logging y monitorización. La comprobación 7 te dice que añadas rate limiting, pero también necesitas saber cuándo alguien está sondeando tus defensas. Registra los intentos de autenticación, los patrones de acceso a datos y las tasas de error. Envía los logs a un servicio que pueda alertarte cuando los patrones cambien.
Cumplimiento normativo. Si manejas datos de salud (HIPAA), datos de tarjetas de pago (PCI DSS) o datos de usuarios europeos (RGPD), tienes requisitos regulatorios más allá de este checklist. No asumas que el código generado por IA cumple la normativa — compruébalo.
Escaneo automatizado. Este checklist es manual. Una vez que lo hayas pasado, configura escaneo de seguridad automatizado en tu pipeline de CI/CD — SAST, DAST, comprobaciones de dependencias en cada pull request. Cubrí por qué las aplicaciones vibe-coded necesitan configuraciones de escáner diferentes al código tradicional en la Parte 6.
Modelado de amenazas. La Parte 7 cubrió cómo construir un modelo de amenazas antes de escribir código. Si te saltaste ese paso, vuelve y hazlo ahora. El checklist detecta problemas comunes; un modelo de amenazas detecta los específicos de tu aplicación.
Lo Único Que Debes Recordar
Cada comprobación de esta lista existe porque he visto una aplicación vibe-coded fallar en ella en producción. No en teoría — en producción, con datos reales de usuarios expuestos. Las vulnerabilidades de QuickNote de esta serie, las brechas de la Parte 3, los fallos de autenticación de la Parte 5 — todos se mapean a elementos de esta lista.
La IA construyó tu aplicación. No la aseguró. Eso es tu trabajo, y este checklist es el mínimo. Ejecútalo antes del lanzamiento. Ejecútalo otra vez después de cada funcionalidad importante. Conviértelo en un hábito, y tu MVP vibe-coded será más seguro que la mayoría de las aplicaciones programadas tradicionalmente que audito.
Como siempre: no te fíes de nada, verifica todo.
- X (Twitter): @SimonRoses
Lecturas Recomendadas
- ¿Qué es la Seguridad del Vibe Coding? Una 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
- Autenticación y Secretos: Lo Que la IA Siempre Hace Mal — Parte 5 de esta serie
- Escaneando Aplicaciones Vibe-Coded: Por Qué el SAST/DAST Tradicional Se Queda Corto — Parte 6 de esta serie
- Prompt Engineering para Código Seguro — Parte 7 de esta serie
Referencias
- GitGuardian (2026). The State of Secrets Sprawl 2026.
- Apiiro (2025). 4x Velocity, 10x Vulnerabilities: AI Coding Assistants Are Shipping More Risks.
- HTTP Archive (2025). Web Almanac 2025 — Security.
- Sonatype (2026). State of the Software Supply Chain.
- Akamai (2024). State of the Internet — Credential Stuffing.
- RedHunt Labs (2022). Analysing Misconfigured Firebase Apps — Project Resonance Wave 10.
- NIST NVD (2025). CVE-2025-31324 — SAP NetWeaver Unrestricted File Upload


