¿Qué es una función hash?

Ayer comentaba en Facebook que una cadena alfanumérica que aparecía en una noticia «parecía el resultado de alguna función hash» y hubo gente que me preguntó qué era eso de una función hash. Así que he pensado que lo mejor será contarlo por aquí que me puedo extender más.

Cifrado

Las funciones hash, también llamadas «digest» o «de resumen«, son algoritmos que calculan y devuelven una cadena de texto alfanumérica de longitud fija calculada a partir de un dato de entrada, que puede ser un texto o un fichero binario. Se trata de funciones unidireccionales, lo que quiere decir que aun teniendo el resultado y conociendo el algoritmo no debe ser posible revertir las operaciones para descifrar el dato de entrada.

Son también funciones determinísticas, siempre devuelven la misma salida para la misma entrada. Al ser resúmenes existe la posibilidad de que dos entradas totalmente distintas devuelvan el mismo resultado, es lo que se llama una «colisión«. Teóricamente es imposible crear una función hash sin colisiones, puesto que las posibilidades de entrada son infinitas pero las de salida están limitadas a la longitud de la cadena de resumen, por tanto a mayor logitud de respuesta menor posibilidad de colisión y mayor robustez para el algoritmo. Las colisiones son lo que facilita un tipo de ataque criptográfico llamado «Ataque de Cumpleaños» del que puedes leer en ese enlace.

Por ejemplo, si calculo el hash de la cadena «hola» con el algoritmo md5 el resultado será: 4d186321c1a7f0f354b297e8914ab240. En cambio si le paso la cadena «Hola«, con mayúscula, el resultado devuelto será: f688ae26e9cfa3ba6235477831d5122e.

¿Qué utilidad tiene esto? Bueno, no se trata de un algoritmo para cifrar un mensaje y poder descifrarlo luego, como pueda ser el caso de AES, no tiene el mismo propósito que la criptografía simétrica o asimétrica. Las funciones hash son útiles por ejemplo para almacenar contraseñas en una base de datos, ya que aunque alguien lograra acceder a ellas no podría descifrar cual es la contraseña original. Otro de sus principales usos es asegurar que un fichero no ha sido modificado durante el trayecto desde su envío: se calcula el hash antes de enviarlo, si al recibirlo lo volvemos a calcular y el resultado no es el mismo implica que alguien ha capturado (un ataque de intermediario o man in the middle) y modificado el fichero. Las funciones hash también se utilizan en los procesos de firma digital de documentos.

¿Cuales son los algoritmos de resumen más populares o más comunes? Te dejo una lista con sus enlaces a Wikipedia:

Función PHP para hash de contraseñas con GOST y salt

Se está poniendo de moda GOST (así, sin H, que no es inglés) desde todo el asunto Snowden porque, según han empezado a decir algunos expertos en seguridad, es uno de los algoritmos que la NSA no ha logrado romper (al menos no se sospecha que lo lograran). Se trata de un algoritmo criptográfico parido por matemáticos soviéticos en los últimos años de la Guerra Fría, revisado posteriormente un par de ocasiones. Para más info, como siempre, la wikipedia.

El caso es que con el tema de la paranoia de la seguridad me han pedido una función para encriptar passwords segura en PHP, aunque en PHP 5.5 ya tengamos unas muy cómoda y seguras funciones para trabajar con passwords. Pero cliente manda, y ha leído que GOST tralarí y no se fía de algo que sea standar… así que me he hecho una función propia, que os comparto por si queréis usarla.

<?
function getSecurePass($password_plano, $username){
    if(strlen($password_plano)<8){
	//exigimos un mínimo de 8 caracteres
        $response = array("aceptado"=>FALSE, "Resultado"=>"Error: Contraseña demasiado corta");
    }else{	
                if(strlen($password_plano) % 2){
                     $salt0="@fr!87$"; //aquí metemos una cadena, la que vosotros prefiráis
                }else{
                     $salt0="~m¿0kL" //aquí metemos otra, y según sea par o impar la longitud cogerá la que corresponda
                }		
		$salt1 = substr($password_plano, strlen($texto_plano)-6, 5);
		$salt2 = substr(md5($username), 6, 6);
		$salt3 = substr($username, strlen($username),-2);
		$arrayPss = str_split($password_plano,(strlen($password_plano)/2)+1);
		$hash = hash('gost', $salt3.$arrayPss[0].$salt0.$salt1.$arrayPss[1].$salt3.$salt2);
		sleep(1);
		$response = array("aceptado"=>True, "Resultado"=>$hash);
	}
    reutrn $response;
}

?>

Y para complementar el sistema de login contra ataques por fuerza bruta es una buena idea ralentizar la respuesta desde el servidor. ¿Y cómo hacerlo sin darle la vara al usuario mucho? Haciendo una función de login que espere, pero sólo cuando el password sea erróneo. En este caso no desarrollé nada y copié un ejemplo desde php.net que ahí os pego (en mi caso lo adapté para usarlo con el resto del sistema de login desarrollado, en los comentarios del enlace está la función original son su explicación):

<?php
public function handle_login() {
    if($uid = user::check_password($_REQUEST['email'], $_REQUEST['password'])) {
        return self::authenticate_user($uid);
    }
    else {
        // delay failed output by 2 seconds
        // to prevent bruit force attacks
        sleep(2);
        return self::login_failed();
    }
}
?>

Espero que os sirva de ayuda a la hora de asegurar vuestras contraseñas.