Contrariamente a lo que enfatizan las respuestas más votadas aquí, la no inyectividad (es decir, que hay varias cadenas de hash con el mismo valor) de una función hash criptográfica causada por la diferencia entre el tamaño de entrada grande (potencialmente infinito) y el tamaño de salida fijo no es el punto importante : en realidad, preferimos las funciones hash en las que esas colisiones ocurren lo menos posible.
Considere esta función (en notación PHP, como la pregunta):
function simple_hash($input) {
return bin2hex(substr(str_pad($input, 16), 0, 16));
}
Esto agrega algunos espacios, si la cadena es demasiado corta, y luego toma los primeros 16 bytes de la cadena, luego la codifica como hexadecimal. Tiene el mismo tamaño de salida que un hash MD5 (32 caracteres hexadecimales, o 16 bytes si omitimos la parte bin2hex).
print simple_hash("stackoverflow.com");
Esto dará como resultado:
737461636b6f766572666c6f772e636f6d
Esta función también tiene la misma propiedad de no inyectividad resaltada por la respuesta de Cody para MD5: podemos pasar cadenas de cualquier tamaño (siempre que quepan en nuestra computadora), y generará solo 32 dígitos hexadecimales. Por supuesto que no puede ser inyectable.
Pero en este caso, es trivial encontrar una cadena que se asigne al mismo hash (solo aplíquelo hex2bin
en su hash y lo tendrá). Si su cadena original tenía la longitud 16 (como nuestro ejemplo), incluso obtendrá esta cadena original. Nada de este tipo debería ser posible para MD5, incluso si sabe que la longitud de la entrada es bastante corta (excepto probando todas las entradas posibles hasta que encontremos una que coincida, por ejemplo, un ataque de fuerza bruta).
Los supuestos importantes para una función hash criptográfica son:
- es difícil encontrar una cadena que produzca un hash determinado (resistencia a la preimagen)
- es difícil encontrar una cadena diferente que produzca el mismo hash que una cadena dada (segunda resistencia a la preimagen)
- es difícil encontrar un par de cadenas con el mismo hash (resistencia a colisiones)
Obviamente mi simple_hash
función no cumple ninguna de estas condiciones. (En realidad, si restringimos el espacio de entrada a "cadenas de 16 bytes", entonces mi función se vuelve inyectiva y, por lo tanto, es resistente a la segunda preimagen y a las colisiones).
Ahora existen ataques de colisión contra MD5 (por ejemplo, es posible producir un par de cadenas, incluso con un mismo prefijo dado, que tienen el mismo hash, con bastante trabajo, pero no imposible), por lo que no debería usar MD5 para cualquier cosa crítica. Aún no hay un ataque de preimagen, pero los ataques mejorarán.
Para responder a la pregunta real:
¿Qué tienen estas funciones que hacen que sea imposible volver a rastrear las cadenas resultantes?
Lo que MD5 (y otras funciones hash basadas en la construcción Merkle-Damgard) hacen efectivamente es aplicar un algoritmo de cifrado con el mensaje como clave y algún valor fijo como "texto sin formato", utilizando el texto cifrado resultante como hash. (Antes de eso, la entrada se rellena y se divide en bloques, cada uno de estos bloques se utiliza para cifrar la salida del bloque anterior, XORed con su entrada para evitar cálculos inversos).
Los algoritmos de encriptación modernos (incluidos los que se usan en las funciones hash) están diseñados para dificultar la recuperación de la clave, incluso con texto plano y cifrado (o incluso cuando el adversario elige uno de ellos). Por lo general, lo hacen haciendo muchas operaciones de mezcla de bits de manera que cada bit de salida esté determinado por cada bit de clave (varias veces) y también por cada bit de entrada. De esa manera, solo puede volver sobre lo que sucede en el interior si conoce la clave completa y la entrada o la salida.
Para funciones hash similares a MD5 y un ataque de preimagen (con una cadena hash de un solo bloque, para facilitar las cosas), solo tiene entrada y salida de su función de cifrado, pero no la clave (esto es lo que está buscando).