Segundo factor omitido: la sesión se emitía antes de validar el código
El primer paso del acceso ya devolvía la cookie de sesión definitiva. La pantalla del código era una barrera visual: saltarla daba acceso a la cuenta.
Resumen
El acceso estaba dividido en dos pasos: usuario y contraseña primero, código de un solo uso después. La comprobación se hacía en el servidor y rechazaba correctamente los códigos que no coincidían.
El fallo estaba antes: la respuesta del primer paso ya incluía la cookie de sesión definitiva. El segundo factor no autorizaba nada. Solo decidía qué pantalla se mostraba a continuación.
La severidad 9.1 responde a lo comprobado: acceso remoto a la cuenta completa con solo la contraseña.
Detalles técnicos
La sesión se emitía demasiado pronto
La respuesta al envío de usuario y contraseña incluía la cookie de sesión, sin ninguna marca de verificación pendiente:
POST /auth/login HTTP/2
{"user":"[CUENTA DE PRUEBA]","pass":"[REDACTED]"}
HTTP/2 200 OK
Set-Cookie: session=[TOKEN]; Path=/; HttpOnly; Secure
{"step":"otp_required"}El segundo paso no aportaba autorización
Detuvimos el flujo en ese punto y fuimos directamente al área privada. La sesión ya era válida:
GET /portal/dashboard HTTP/2
Cookie: session=[TOKEN DEL PRIMER PASO]
HTTP/2 200 OK
[+] Sesión aceptada sin haber enviado ningún código
[+] El campo "step" solo gobernaba la pantalla del clienteAlcance dentro de la cuenta
Esa sesión funcionaba contra los endpoints de datos personales y de gestión, no solo contra la vista inicial:
GET /api/profile -> 200 OK datos personales completos
GET /api/documents -> 200 OK documentación de la cuenta
POST /api/profile/email -> 200 OK cambio de correo aceptado
[!] Cambiar el correo permitiría además apropiarse del restablecimiento de contraseñaLo comprobamos todo sobre una cuenta de prueba creada para la auditoría, el cambio de correo incluido. No accedimos a ninguna cuenta de cliente y avisamos de inmediato por el canal acordado en las reglas de compromiso.
El caso se publica anonimizado, sin nombres ni dominios, con el fallo ya corregido y con la autorización previa del cliente.
Impacto
- Segundo factor sin efecto: bastaba con la contraseña para entrar
- Acceso completo a la cuenta: la documentación asociada quedaba a la vista
- Toma de cuenta persistente: cambiar el correo de recuperación dejaba al atacante dentro
- Credenciales filtradas reutilizables: el segundo factor dejaba de proteger frente a contraseñas comprometidas
- Registro de auditoría no fiable: nada en el servidor distinguía un acceso verificado de uno que no lo era
Remediación
- No emitir la sesión hasta completar la autenticación. El primer paso debe devolver un identificador temporal de flujo, no la cookie definitiva.
- Marcar el estado en el propio token. Si se emite algo antes, tiene que llevar un estado «pendiente de verificación» que el servidor compruebe en cada petición.
- Comprobar el estado en el servidor en todos los endpoints privados, no en la capa de presentación.
- Invalidar el flujo tras varios intentos fallidos y limitar su vigencia.
- Registrar por separado el inicio del flujo y su verificación, para que el registro refleje lo que realmente ocurrió.
Referencias
Más análisis
¿Tu acceso en dos pasos emite la sesión antes de validar el código?
Dinos qué activos quieres auditar. Respondemos en menos de 24 horas.