Introducción
En una ocasión estaba leyendo sobre los ataques de temporización y la recomendación de usar hash_equals en lugar de simples comparaciones con PHP. Más tarde me puse a investigar y supe la razón de usar hash_equals cuando estamos tratando con contraseñas (que al final son cadenas).
Lo que es un ataque de temporización o timming atack
Esto es independiente del lenguaje, o eso creo. No daré una definición formal sino una que espero todos puedan entender.
Un ataque de temporización es aquel en donde un atacante logra, a través de suposiciones y acercamientos, adivinar una contraseña.
Veamos cómo funciona… para esto, definamos que nuestra contraseña será “AAA123”.
Cómo se comparan las cadenas
Como seres humanos, si nos dieran a comparar una cadena, compararíamos carácter por carácter. No lo hacemos siempre, pero si fuera una cadena larga y sin sentido sí lo haríamos.
Por ejemplo, “Hola” y “Hla”… comparamos carácter por carácter, primero la H y la H. Luego la o y la l, y justo ahí decimos que no son iguales. ¿gastamos tiempo comparando las demás letras? no, porque ya desde la o nos dimos cuenta de que no son iguales.
Los lenguajes de programación (o al menos PHP) también son como nosotros: comparan y la primera vez que encuentran algo distinto regresan, no siguen comparando.
Esto se hace siempre cuando usamos ===
o incluso strcmp
.
Contraseñas como cadenas
Las contraseñas son cadenas, simples cadenas. Ya sea hasheadas o no, son cadenas al final de todo. Imaginemos que están en texto plano, y que por cada comparación de un carácter se utiliza 1 segundo sólo para efectos de simplicidad.
- Contraseña correcta: AAA123
- Contraseña que un atacante pone: 123
El lenguaje comparará carácter por carácter, primero la A y luego el 1. No coinciden así que desde ahí regresa falso. ¿cuánto tiempo tardó? exacto, 1 segundo.
Ahora el atacante ve que esa contraseña no es correcta e intenta con otra…
- Contraseña correcta: AAA123
- Contraseña que un atacante pone: ASDFG
PHP va y compara la A con la A, muy bien, eso sí coincide. Ahora con A y S, eso no coincide. ¿cuánto tiempo tardó? 2 segundos.
Entonces aquí el atacante se da cuenta de algo… aunque no ponga la contraseña correcta, a veces tarda más y a veces menos. Analizando más a fondo se da cuenta de que si pone el inicio correcto, tardará más. Hasta ahora sabe que la contraseña comienza con A. Entonces prueba de nuevo:
- Contraseña correcta: AAA123
- Contraseña que un atacante pone: AAAAAA
En este caso las primeras 3 letras coinciden, y al comparar 1 con A ya no pasa, pero tarda 4 segundos. Así que eso significa que las 3 primeras están bien. Prueba de nuevo:
- Contraseña correcta: AAA123
- Contraseña que un atacante pone: AAA111
Ya casi lo logra, ahora tardará 5 segundos (porque los primeros 4 son correctos pero con el quinto se detiene, al comparar 1 y 2). Y bueno, para no hacerlo largo, al final el atacante podrá adivinar la contraseña porque el sistema le irá indicando cómo va todo.
Otra explicación
Es como jugar a frío y caliente. Le vas preguntando al sistema si la contraseña empieza con determinada letra, y él te dice si sí o no según el tiempo que se tarde. Nunca he visto un ataque de estos, porque las computadoras de hoy son rápidas pero supongo que sí se puede medir.
En resumen, entre más correcta sea la contraseña, más tiempo tardará en responder.
Mitigar estos ataques
La función hash_equals nos asegura tardar el mismo tiempo cuando compare cadenas, sin importar si desde el inicio no coinciden. Pero cuidado, que si las cadenas no miden lo mismo se regresará false inmediatamente.
Utiliza hash_equals
, pásale como primer argumento la contraseña que tienes guardada en la base de datos (o la contraseña en la que confíes) y como segundo argumento la contraseña que el usuario proporciona (no lo hagas al revés)
Esta función regresa un booleano indicando si coinciden o no.
Te recomiendo que uses eso si comparas contraseñas con tu propio cifrado; porque si quieres un método confiable que se implementa sin esfuerzo puedes usar password_hash y password_verify.
Pingback: Ejemplo simple de login con PHP - Parzibyte's blog