Hola, somos Arume

Desarrollamos páginas web, aplicaciones para móviles, capas de realidad aumentada y aplicaciones para Facebook. Nos apasiona la informática y somos unos perfeccionistas incurables; por eso en nuestros proyectos utilizamos estándares.

tel. 625 519 694

Mendaña de Neyra, 34, 3º B, 15008, A Coruña

Autenticarse

Registrarse. ¿Has olvidado tu contraseña?

Etiquetas

Saltar las etiquetas

Suscríbete a las RSS

Estás en:

  • Inicio >
  • Blog >
  • Encriptar y guardar contraseñas en base de datos

Encriptar y guardar contraseñas en base de datos

16 Feb 2011 por Luis

Comentarios: 14

Top secret

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.

Protegiendo las contraseñas

Nunca debemos guardar las contraseñas de los usuarios sin cifrar. Esto es un error más común de lo que pueda parecer ya que los clientes suelen pedir como requisito el que se pueda "recordar" la contraseña a los usuarios (enviar por correo la contraseña actual, no una nueva).

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.

Comentarios

14 comentarios. Comentar.

1. Danielo el 01 Jul 2011 a las 23:26:16

Genial! Justo la información que buscaba como punto de partida para empezar con el modulo de identificación de mi sitio web. Muchas gracias por el artículo.

2. Luis el 04 Jul 2011 a las 10:58:10

Nos alegra ser de ayuda.

De todas formas recuerda que en los casos en que es importante la seguridad, siempre es mejor utilizar software ya creado y testeado por la comunidad que reinvertar la rueda.

3. Emilio el 26 Ago 2011 a las 05:14:07

Muy buen post. Es muy importante tener éstas cosas en cuenta para garantizar al menos cierto nivel de seguridad por nuestra parte. Lástima que la gente hace pavadas como poner la misma contraseña tanto para la cuenta del banco como para Facebook, y encima conectarse desde redes sin encriptar ó con WEP (un par de segundos de diferencia nomás xD) en lugares muy concurridos, ó entrar a páginas de login clonadas.

Me sirve a mí también el artículo :)

Saludos.

4. Anónimo el 12 Jun 2012 a las 01:05:24

pues mui buena su explicacion pero creo q aun le fla musho massss

5. Anónimo el 20 Ago 2012 a las 15:32:45

hola, muy buena explicacion.

me gustaria saber como hacer para que una sea reemlasada por una nueva en la base de datos...

logre que eliminara la contraseña vieja pero ahora no me quiere guardar la nueva.....

que tengo a hacer..????

6. Luis el 21 Ago 2012 a las 10:54:23

Hola Anónimo.

No entiendo nada de lo que dices en tu comentario. Si quieres que te ayuden deberías explicarte mejor y al menos esforzarte un poco al describir tu problema.

Supongo que "reemlasada" quiere decir "reemplazada", pero aún así no puedo ayudarte, ya que en el artículo no hablo para nada sobre consultas a una base de datos y tú tampoco nos explicas cómo eliminas la vieja ni cómo intentas guardar la nueva.

7. Alex!!! el 29 Ago 2012 a las 02:34:28

holaaaaa como van buen post.. yo ando keriendo saber si en mi blog puedo meter un boton de usuario y contraseña y q dicha informacion se almacene en algun lado para tener un control de la misma..

osea en simples palabras.. quiero que la gente q entre en mi blog y deva poner cualquier usuario y contraseña como primera ves y quiero tener control de esa informacion.... si me ayudan se los agradeceria...

llevo tiempo tratando de hacerlo.. pero como saben en internet hay mucha basura inserbible...!!!!

bueno si pueden ayudarme.. o entender bn lo que quiero hacer por favor podrian escribirme a mi mail o almsn asi poder hablar trankilos..

desde ya muchas gracias.. agradecidoooo!!! ^^ que sigan bien!!!

P/D es muy importante que me ayuden... ^^ muchas gracias nuevamente....

8. Luis el 29 Ago 2012 a las 11:15:56

Hola Alex!!!

No se que tipo de blog estarás usando para que no traiga esa funcionalidad por defecto.

De todas formas, nosotros somos una empresa, nos ganamos la vida haciendo y modificando páginas web. Si tienes alguna duda puntual podemos ayudarte a resolver el problema (siempre a través del blog para que sirva como referencia a otras personas con el mismo problema), pero lo que no podemos hacer es trabajar gratis para ti ni formarte en programación web.

Si lo que quieres es contratarnos para que hagamos algún trabajo en tu página web, puedes ponerte en contacto con nosotros a través de nuestro email o por teléfono (nuestros datos están debajo del logo).

Aprovecho para hacerte un favor y elimino tu email de tu comentario.

Un saludo.

9. tecop el 09 Feb 2013 a las 09:20:12

Hola, al recuperar el "salt" de la base de datos y hacer "hash('sha512', $db_salt.$password)", me sale un "hash"diferente.

La cosa es que lo compruebo aparte con el "salt" devuelto y si me sale el "hash".

No se que es, ¿Podrían ser espacios en blanco que no se vean?

Gracias y saludos

10. Luis el 09 Feb 2013 a las 11:51:16

Hola tecop.

Lo siento pero no entiendo el problema. Según me dices en un "sitio" haces "hash('sha512', $db_salt.$password)" y te sale un hash pero en otro "sitio aparte" haces eso mismo y te da otro hash distinto. ¿Es así? Porque si es así creo que está claro que $db_salt y/o $password no valen lo mismo en el "sitio" y en el "sitio aparte".

Sin más datos no puedo ver cual es el problema. Lo que yo haría sería hacer un "echo" de $db_salt y $password en el "sitio" para comprobar que esos datos son correctos antes de cifrarlos. Si ese no es el problema vería si hay algún "caracter especial" (vocales con tilde, eñes, ...) y si tienes algún problema de codificación de caracteres (que la base de datos esté en utf-8 y el archivo en ISO-8859-1 o algo así).

Un saludo.

11. Mario el 06 Ago 2013 a las 17:46:29

Buenas amigo que tal... tengo una duda acerca de las variables $db_salt y $salt ambas debo crearlas en mi BASE DE DATOS??? si me podrias explicar un poco mas te agradeceria y disculpa

12. Luis el 06 Ago 2013 a las 19:30:48

Hola Mario.

$salt es una variable que debes generar tú de alguna forma, preferiblemente aleatoria (en el ejemplo que hemos puesto: $salt = md5(uniqid(rand(), true)) ). Luego, esta cadena aleatoria deberás usarla como se explica en el ejemplo y guardarla en algún sitio donde puedas recuperarla. En el ejemplo que hemos puesto la guardamos en base de datos. Luego, para comprobar si la contraseña que te introducen es correcta tendrás que generar el hash con la contraseña más el $salt. Como nosotros tenemos el $salt en la base de datos, al recuperarlo le llamamos a esa variable $db_salt, para no confundir con $salt.

Espero que te haya quedado claro.

Un saludo.

13. Mario el 07 Ago 2013 a las 17:23:19

Hola Luis... si ya entiendo perfectamete!!! graciass!!!!

saludos!!!!

14. MartinnitraM el 24 Nov 2014 a las 18:51:43

Hola...

hay algo que no entiendo... la idea de añadir el SALT es, textual, "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"

Pero claro, si yo guardo ese SALT en la base de datos y un atancante accede a ella, también tiene acceso aese SALT así como a la contraseña encriptada con él, así que la supuesta ventaja de que es más larga, etc, no es tal.

¿O lo he entendido mal? Cuando guardo la contraseña y la encripto... ¿realmente hay que guardar el salt con la contraseña?

Un saludo y gracias

Comentar

Comentar de forma anónima

Puedes comentar poniendo cualquier nombre o apodo, exceptuando los nombres de usuarios registrados. Máximo de 50 caracteres.

Comentar como usuario registrado

Registrarse. ¿Has olvidado tu contraseña?