Contraseñas seguras en PHP

Guardar las contraseñas de forma segura en base de datos es uno de los procesos más habituales y más importante de cualquier aplicación con usuarios. En PHP es muy sencillo hacerlo, pero antes vamos a explicar cuales son los problemas y las soluciones para generar y guardar contraseñas seguras.

Lo primero y más importante es que nunca debemos guardar en plano las contraseñas en la base de datos. El motivo es que si nuestra base de datos se ve comprometida el nombre de usuario y la contraseña puede ser usado en otras webs ya que la mayoría de la gente los reutiliza. Que levante la mano quien no tenga la misma contraseña en decenas de webs.

Entonces, ¿cómo las guardamos?. Podríamos hacerlo utilizando alguno de los algoritmos de hash más comunes como son md5 o sha1 pero por una parte estos algoritmos llevan rotos desde hace años y por otra se podrían utilizan tablas arcoíris (rainbow tables) que no son ni más ni menos que tablas de contraseñas con el hash ya generado de forma que es mucho más sencillo realizar un ataque de fuerza bruta.

Para evitar este tipo de ataques utilizaremos Salt. Pero ¿qué es Salt? Esta técnica consiste en añadir una cadena aleatoria a la contraseña antes de que se utilice para generar el hash. Pongamos un ejemplo sencillo para que se entienda, digamos que la contraseña es la palabra «hola», lo que vamos a hacer es generar una cadena aleatoria que deberemos guardar en base de datos para poder regenerar el hash de nuevo. Ahora lo que hacemos es por ejemplo concatenar la contraseña con la cadena aleatoria y sobre el resultado de la concatenación generamos el hash. Vendría a ser algo así:

hash(«hola» . «A%$bcJeL»)

La función hash podría ser cualquier función hash segura, el resultado de la cual es lo que guardaremos en base de datos como contraseña y como ya habíamos mencionado también guardaremos el Salt «A%$bcJeL» en el ejemplo para en algún momento poder regenerar el hash y así poder comprobar la contraseña. Para comprobarla lo que haríamos es recuperar el hash y el Salt de base de datos, regenerar el hash con la contraseña que introduzca el usuario en el formulario correspondiente y comparar. Sería algo así:

hash($campoFormulario . $saltDeBaseDeDatos) == $contrasenyaDeBaseDeDatos

Ahora la tabla arcoíris ya no sirve porque los hashes resultantes al tener Salt serán completamente diferentes.

Una vez hemos entendido todo esto ¿cual es la forma correcta de hacerlo en PHP? Pues usar las funciones password_hash y password_verify. La primera genera un hash con Salt de forma automática, nosotros lo único que tenemos que darle es la contraseña en plano y el algoritmo seguro a utilizar para generar el hash. Actualmente se recomienda el algoritmo CRYPT_BLOWFISH pero si le indicamos PASSWORD_DEFAULT como algoritmo entonces la función utilizará el aceptado como más seguro en ese momento de forma que si sale uno mejor será ese el que se empiece a utilizar.

password_hash ( string $password , integer $algo [, array $options ] ) : string

En el ejemplo sería:

password_hash(«hola», PASSWORD_BCRYPT)

Esto nos generar un hash con este formato:


Como veis tenemos todo lo necesario en un único hash. El tipo de algoritmo que en nuestro caso es CRYPT_BLOWFISH, las opciones del algoritmo si las hubiera, el Salt y el hash de la contraseña. Ahora tan solo necesitamos guardar este hash completo en la base de datos en lugar del hash y el Salt por separado.

Si queremos comprobar la contraseña usaremos la función password_verify a la que tan solo tendríamos que pasarle por un lado la contraseña y por otro el hash completo ya que ahí tiene todos los datos necesarios para la comprobación.

password_verify ( string $password , string $hash ) : bool

Esta función internamente hace algo parecido a lo que hacíamos nosotros en el ejemplo de comprobación con Salt devolviéndonos al final verdadero o falso dependiendo de si la contraseña coincide o no.

Con todo esto ya sabéis como generar un hash seguro y como comprobar la validez de vuestras contraseñas a partir del hash. Pero si tienes cualquier duda déjamela en un comentario.

Nos vemos.