Ataques de tiempo en PHP: El "Leak" Silencioso en tus Endpoints de Login
En el mundo de la ciberseguridad, solemos preocuparnos por las inyecciones SQL o el Cross-Site Scripting (XSS). Sin embargo, existe una vulnerabilidad mucho más sutil, a menudo ignorada por ser considerada "académica", pero que en la práctica permite mapear bases de datos enteras de usuarios, los Timing Attacks (ataques de tiempo).
Recientemente, un caso real en una SaaS basada en Laravel puso esto de manifiesto, un investigador descubrió que el endpoint de login revelaba si un email existía o no, no por el mensaje de error (que era idéntico), sino por el tiempo de respuesta.
1. El problema: La brecha de los 64 milisegundos
El error común en PHP y otros lenguajes es el retorno temprano (early return). Observa este patrón típico:
function vulnerableLogin(string $email, string $password): bool {
$user = User::where('email', $email)->first();
if ($user === null) {
return false; // El servidor responde en ~1ms
}
// password_verify es intencionalmente lento (ej. bcrypt)
return password_verify($password, $user->password_hash); // El servidor responde en ~65ms
}
La diferencia es abismal. Mientras que el ruido de una red doméstica suele rondar los 5ms, una señal de 64ms es un "faro" para un atacante. Con un script sencillo, se pueden validar miles de correos por hora para saber quién tiene cuenta en tu plataforma.
¿Por qué es peligroso?
- Phishing dirigido: Saber que alguien es cliente de un banco específico hace que el ataque sea mucho más creíble.
- Enumeración de usuarios: Permite limpiar listas de correos filtrados para atacar solo las cuentas que realmente existen (Credential Stuffing).
2. Cómo solucionarlo: La estrategia "Dummy Hash"
Para mitigar la enumeración de usuarios, debemos garantizar que el servidor trabaje igual, exista el usuario o no. La solución es ejecutar siempre password_verify contra un hash ficticio si el usuario no se encuentra.
final class LoginService{
// Hash pre-generado con el mismo factor de coste que tus usuarios reales
private const DUMMY_HASH = '$2y$10$YlEKgNfFwFVGnxPHGHuMgegzqRpHkoJBXxCJqnqVLxSqBL0jZc3pe';
public function authenticate(string $email, string $password): ?User
{
$user = User::where('email', $email)->first();
// Si el usuario no existe, usamos el dummy hash para simular el trabajo de CPU
$hashToCheck = $user?->password_hash ?? self::DUMMY_HASH;
$valid = password_verify($password, $hashToCheck);
if ($user === null || !$valid) {
return null;
}
return $user;
}
}
Al hacer esto, el ratio de diferencia de tiempo pasa de 1,500,000x a prácticamente 1x, haciendo la distinción imposible bajo el ruido normal de internet.
3. Comparación de Strings: === vs hash_equals
Otro ataque de tiempo ocurre al comparar tokens de API o códigos 2FA. El operador === deja de comparar en cuanto encuentra el primer carácter diferente.
Para evitar que un atacante adivine un token carácter por carácter, PHP ofrece la función hash_equals(), que tarda lo mismo sin importar cuántos caracteres coincidan.
- Mal: if ($providedToken === $secretToken)
- Bien: if (hash_equals($secretToken, $providedToken))
4. Más allá del Login: Checkpoint de seguridad
Los ataques de tiempo no se limitan al formulario de acceso. Revisa estos puntos en tu aplicación:
- Recuperación de contraseña: ¿Tarda más el sistema cuando el email introducido existe? (Incluso si el mensaje dice "Si tu cuenta existe, recibirás un correo").
- Registro de usuarios: ¿La validación de "email ya registrado" ocurre antes de procesos pesados?
- Respuestas HTTP: Asegúrate de que tanto el tamaño del cuerpo de la respuesta como las cabeceras sean consistentes. Una respuesta de error de 200 bytes vs una de 500 bytes es otra forma de fuga de información.
Otros artículos que pueden ser de tu interes:
Conclusión
Creeo que la seguridad no es solo evitar brechas críticas, sino cuidar los pequeños detalles que protegen la privacidad de los usuarios. Los Timing Attacks no son teóricos; son una herramienta real en el arsenal de cualquier atacante moderno y automatizado.
Implementar hash_equals y la técnica del Dummy Hash solo toma una tarde, pero cierra una puerta que, de otro modo, permanecería abierta silenciosamente durante años.