Siempre que se diseña una aplicación que necesite identificación de usuarios se suscita la duda de si utilizar un sistema de autenticación externo (Facebook, Google , OpenId, …) o un sistema de autenticación propio. Cuando se utiliza un sistema de autenticación propio, se plantea el problema de la seguridad a la hora de guardar las contraseñas de los usuarios en nuestra base de datos.
Aparentemente es un problema menor, ya que, al fin y al cabo, la contraseña de un usuario para nuestro foro o blog no parece una información crítica. Y esto sería así si no fuese porque se sabe que muchos usuarios reutilizan sus contraseñas en diferentes sitios. Si lo unimos a que la cuenta de email es también un dato que se suele pedir, cualquier atacante que consiga robar las contraseñas de un pequeño foro o blog acabará con las contraseñas de las cuentas de email, facebook, … de muchos de sus usuarios.
Además de las medidas para evitar los ataques por fuerza bruta (pedir contraseñas con un número mínimo de caracteres, que contengan mayúsculas, minúsculas, caracteres especiales y números, bloquear repetidos intentos de acceso fallido, …) y otros, hay que tener en cuenta la posibilidad de que se comprometa nuestra base de datos o un backup de la misma.
Las contraseñas siempre han de guardarse en la base de datos «cifradas» de algún modo, de forma que el atacante no pueda conocerlas. Si es requisito indispensable el que las contraseñas puedan ser «recordadas», deberán cifrarse con un algoritmo de «doble sentido» para que puedan ser descifradas por la aplicación. Pero este sistema es muy poco recomendable ya que el atacante podrá también descifrarlas. Lo mejor es cifrar las contraseñas con un algoritmo de «un sólo sentido» de forma que no se puedan descifrar.
ENCRIPTANDO LAS CONTRASEÑAS
En realidad, aunque seguiremos llamándole «encriptado» por comodidad, lo que se suele hacer es pasar las contraseñas por una función resumen como MD5, SHA, …, guardar en la base de datos el «resumen» generado y descartar la contraseña.
$hash = sha1($password);
unset($password);
// Si $password == 'apple'
// $hash == 'd0be2dc421be4fcd0172e5afceea3970e2f3d940'
Más adelante, para comprobar si un usuario ha puesto bien su contraseña, lo que haremos será encriptar lo que el usuario nos envía y comparar el resultado del cifrado con el que tenemos en nuestra base de datos.
// $db_hash será lo que tenemos guardado en la base de datos
if ($db_hash === sha1($password))
{
// Contraseña correcta
}
Así, aunque se comprometa nuestra base de datos, el atacante no podrá conocer las contraseñas de los usuarios de forma fácil. Lo único que le queda para poder descifrarlas es un ataque por fuerza bruta o por tablas rainbow (tablas con cadenas de caracteres y el resultado de cifrarlas con algunas funciones de resumen).
ELEGIR UN ALGORITMO DE CIFRADO
Las funciones de resumen más conocidas y utilizadas son MD5 y SHA-1, pero dado que MD5 y SHA-1 han sido comprometidas, actualmente se están imponiendo nuevas funciones como son SHA-2 y Bcrypt.
A la hora de escoger una de ellas para nuestra aplicación, deberemos tener en cuenta además de la disponibilidad que tengamos, la velocidad de ejecución de la función. En este caso, al contrario que en la mayoría de los casos en computación, la que más nos interesa es la más lenta (Bcrypt). La razón es que nosotros sólo vamos a cifrar una contraseña al identificarse un usuario. No importa que este proceso lleve 0.001 segundos o 0.3 segundos. Sin embargo, para un atacante, esta diferencia hace que generar tablas rainbow pueda tardar de unas horas a varios años.
AÑADIENDO SEMILLAS
Otra forma de ponérselo difícil al atacante es añadir una semilla a la contraseña antes de pasarla por la función resumen. De esta forma, aún tiene más complicado utilizar las tablas rainbow, al hacer la cadena de la contraseña más larga y más improbable que esté en la tabla rainbow.
Para ponérselo aún peor, lo mejor es que la semilla sea única para cada contraseña, de forma que no pueda hacerse una tabla rainbow con tu semilla incorporada, sino que tendría que hacerse una tabla para cada contraseña.
La forma de hacerlo sería así:
$salt = md5(uniqid(rand(), true)); // O incluso mejor si tuviese mayúsculas, minúsculas, caracteres especiales...
$hash = hash('sha512', $salt.$password); // Puede ponerse delante o detrás, es igual
unset($password);
// Guardar en base de datos el $hash y $salt
Luego, cuando queramos autenticar un usuario:
// $db_salt será la semilla que tendremos en base de datos
// $db_hash será el resumen que tenemos guardado en la base de datos
if ($db_hash === hash('sha512', $db_salt.$password))
{
// Contraseña correcta
}
Con este método, incluso aunque dos usuarios tengan la misma contraseña, será imposible saberlo ya que su resumen será distinto.
Te esperamos en los siguientes artículos en donde hablaremos mas acerca de estos temas, los cuales hoy en día son de vital importancia en el mundo de la tecnología.
gracias te pasaste, no pude publicar por face
Hola Teksoux, esperamos seguir publicando post de tu agrado.