Aviso: Este proyecto es intencionadamente inseguro y existe para practicar securización en un taller. No lo despliegues en internet ni lo uses con datos reales.
Instala Node.JS si no lo tienes
Crear una aplicación mínima de autenticación (solo login y registro) con React (frontend) y Node/Express (backend) que arranca siendo vulnerable. A partir de aquí, el taller consiste en identificar y mitigar cada fallo.
./server/server.js # API Express insegura
./server/users.json # "Base de datos" de usuarios en texto claro
./client/src/App.jsx # UI Login/Registro (sin mostrar token)
./client/src/App.css # Estilos (modo oscuro)
GET /login?username=...&password=...→ devuelve{ token, username }si coincide.GET /register?username=...&password=...→ crea un usuario nuevo enusers.jsony devuelve{ token, username }.
Nota: El "token" es un base64 casero sin firma ni expiración y, en este baseline, no se usa para proteger recursos posteriores (sirve como anzuelo para la práctica de hardening).
Backend
cd server
npm install
node server.jsFrontend (ReactJS + Vite)
cd client
npm install
npm run devA continuación se listan las vulnerabilidades reales del baseline y cómo pueden explotarse a alto nivel en un entorno de laboratorio.
Qué es: Las credenciales se guardan literalmente como { username, password } en un archivo local.
Impacto: Exposición total de contraseñas; robo masivo de cuentas; reutilización de esas contraseñas en otros servicios.
Vectores:
- Acceso al fichero por insiders/usuarios del host (lectura de
server/users.json). - Compromiso del servidor o copia de seguridad; el atacante obtiene todas las contraseñas sin esfuerzo.
- Fugas accidentales del repositorio o artefactos de CI/CD.
Indicadores: Aparición de users.json en repos públicos; presencia del archivo en logs de backup o artefactos.
Qué es: /login y /register aceptan usuario/contraseña como querystring.
Impacto: Filtrado de credenciales a múltiples superficies.
Vectores:
- Historial del navegador y autocompletado guardan la URL con la contraseña.
- Logs de proxy/servidor/monitoring registran la línea de petición completa.
- El header Referer puede enviar la URL (y la contraseña) a otros sitios al seguir enlaces externos.
- Cachés intermedias podrían almacenar la respuesta de un GET con parámetros sensibles.
Indicadores: Contraseñas visibles en history, analytics o logs de acceso.
Qué es: El baseline asume HTTP en local.
Impacto: Interceptación y manipulación de tráfico.
Vectores:
- Observadores en la misma red (Wi‑Fi pública) capturan usuario/contraseña en texto claro.
- Ataques de man‑in‑the‑middle reescriben respuestas o inyectan contenido malicioso.
Indicadores: Tráfico visible en sniffer; certificados ausentes; mixed content si se prueba sobre dominios.
Qué es: El backend permite peticiones desde cualquier origen y autoriza al navegador a leer las respuestas.
Impacto: Facilita el abuso del API desde sitios de terceros y automatiza enumeración y pruebas de fuerza bruta desde el propio navegador del atacante.
Vectores:
- Páginas maliciosas hacen
fetcha/loginy leen mensajes de error/respuesta para enumerar usuarios o medir contraseñas comprometidas. - Integraciones no autorizadas de terceros consumen el API libremente.
Indicadores: Alto volumen de peticiones con Origin variados; scripts externos consumiendo el API.
Qué es: Respuestas distintas para "usuario no existe" vs "contraseña incorrecta".
Impacto: Permite adivinar cuentas válidas.
Vectores:
- Envío de credenciales con usuarios de diccionario; respuesta
404(no existe) vs401(existe).
Indicadores: Picos de 404/401 correlacionados por username en logs.
Qué es: El backend acepta intentos ilimitados sin penalización.
Impacto: Fuerza bruta y credential stuffing a gran escala.
Vectores:
- Ataques de diccionario sobre
/login. - Reutilización de combos user:pass filtrados (p. ej., listas públicas) contra el endpoint.
Indicadores: Múltiples intentos por IP/usuario en ventanas cortas; ausencia de 429.
Qué es: El token no está firmado, no expira y se guarda en almacenamiento web del navegador.
Impacto: Suplantación si el token se usara para acceso posterior; reutilización indefinida; mayor exposición ante XSS.
Vectores:
- Cualquiera puede forjar tokens (no hay verificación criptográfica) si hubiese rutas protegidas por él.
- Cualquier script inyectado en el frontend podría leer
localStoragey exfiltrar el token.
Indicadores: Tokens que no caducan; ausencia de verificación del lado servidor.
Qué es: /register crea cuentas con una petición GET sin protección adicional.
Impacto: Cambios de estado predispuestos a abusos y a ser cacheables por proxies.
Vectores:
- Disparar altas de usuario de forma cross‑site (no requiere cookies para ejecutarse desde un tercero).
- Automatización masiva de altas para inundar el fichero
users.json.
Indicadores: Crecimiento rápido de users.json; nombres basura/secuenciales.
Qué es: No hay límites ni reglas para username/password.
Impacto: Inestabilidad, almacenamiento de datos mal formados, consumo de recursos.
Vectores:
- Envío de valores muy largos para provocar latencia o agotamiento de disco al persistir.
- Inclusión de caracteres de control que rompan logs o herramientas aguas abajo.
Indicadores: Respuestas más lentas; errores al serializar; crecimiento anómalo del archivo.
Qué es: El servidor guarda en users.json con writeFileSync y mantiene una copia en memoria.
Impacto: Condiciones de carrera, corrupción o pérdidas si hay concurrencia o cierres inesperados; DoS por E/S bloqueante.
Vectores:
- Múltiples registros simultáneos → inconsistencias entre memoria y disco.
- Peticiones rápidas que fuerzan bloqueos sincronizados y degradan el servicio.
Indicadores: Errores intermitentes al registrar; diferencias entre contenido en memoria y archivo.
Qué es: No se aplica CSP, HSTS, X-Content-Type-Options, X-Frame-Options, etc.
Impacto: Menor resiliencia ante futuras clases de ataques (p. ej., XSS o clickjacking) si se introducen más vistas o rutas.
Vectores:
- Cualquier futura inyección en el frontend tendría menos barreras para ejecutar scripts.
Indicadores: Análisis con herramientas (OWASP ZAP, Lighthouse) reporta políticas ausentes.
- Texto claro → copia de
users.json→ robo total de contraseñas. - GET credenciales → history/logs/referer → filtración de secretos.
- Sin HTTPS → sniffing/MITM → robo de cuentas y manipulación.
- CORS
*→ fetch cross‑origin legible → enumeración/abuso del API. - Errores diferenciados → medición
404/401→ enumeración de usuarios. - Sin rate‑limit → fuerza bruta/stuffing → accesos no autorizados.
- Token débil + localStorage → forja/robo por XSS → secuestro de sesión (si se usara).
- Registro por GET → cambios de estado cacheables y cross‑site → spam de cuentas/DoS.
- Sin validación → entradas enormes/raras → inestabilidad/DoS por recursos.
- Escritura síncrona en JSON → carrera/bloqueo → corrupción/caídas.
- Sin headers de seguridad → superficie ampliada → mayor impacto de futuras inyecciones.
Para centrar el taller, no hay rutas protegidas tras el login. El objetivo es practicar endurecimiento de entrada/salida, almacenamiento de credenciales y gestión de sesión, antes de ampliar el dominio funcional.
- Migrar a
POST+ JSON sobre HTTPS. - Hash de contraseña con Argon2id o bcrypt (salt por usuario).
- Mensajes de error genéricos + registro interno.
- Rate‑limit + backoff + bloqueo temporal.
- Restringir CORS por origen/método.
- Autenticación con cookie HttpOnly+Secure (o JWT firmado) y expiración corta.
- Validación de entrada, límites y normalización.
- Política de cabeceras (CSP, HSTS, etc.) y logging seguro.
Uso educativo. No apto para producción.