php

hash_equals y ataques de temporización (timming attacks) en PHP

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.

Estoy aquí para ayudarte 🤝💻


Estoy aquí para ayudarte en todo lo que necesites. Si requieres alguna modificación en lo presentado en este post, deseas asistencia con tu tarea, proyecto o precisas desarrollar un software a medida, no dudes en contactarme. Estoy comprometido a brindarte el apoyo necesario para que logres tus objetivos. Mi correo es parzibyte(arroba)gmail.com, estoy como@parzibyte en Telegram o en mi página de contacto

No te pierdas ninguno de mis posts 🚀🔔

Suscríbete a mi canal de Telegram para recibir una notificación cuando escriba un nuevo tutorial de programación.
parzibyte

Programador freelancer listo para trabajar contigo. Aplicaciones web, móviles y de escritorio. PHP, Java, Go, Python, JavaScript, Kotlin y más :) https://parzibyte.me/blog/software-creado-por-parzibyte/

Ver comentarios

Entradas recientes

Creador de credenciales web – Aplicación gratuita

Hoy te voy a presentar un creador de credenciales que acabo de programar y que…

6 días hace

Desplegar PWA creada con Vue 3, Vite y SQLite3 en Apache

Ya te enseñé cómo convertir una aplicación web de Vue 3 en una PWA. Al…

2 semanas hace

Arquitectura para wasm con Go, Vue 3, Pinia y Vite

En este artículo voy a documentar la arquitectura que yo utilizo al trabajar con WebAssembly…

2 semanas hace

Vue 3 y Vite: crear PWA (Progressive Web App)

En un artículo anterior te enseñé a crear un PWA. Al final, cualquier aplicación que…

2 semanas hace

Errores de Comlink y algunas soluciones

Al usar Comlink para trabajar con los workers usando JavaScript me han aparecido algunos errores…

2 semanas hace

Esperar promesa para inicializar Store de Pinia con Vue 3

En este artículo te voy a enseñar cómo usar un "top level await" esperando a…

2 semanas hace

Esta web usa cookies.