Ola, somos Arume

Desenvolvemos páxinas web, aplicacións para móbiles, capas de realidade aumentada e aplicacións para Facebook. Apaixónanos a informática e somos uns perfeccionistas incurables; por eso nos nosos proxectos utilizamos estándares.

tel. 625 519 694

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

Autenticarse

Rexistrarse. Esqueceches o teu contrasinal?

Etiquetas

Saltar as etiquetas

Subscríbete ás RSS

Estás en:

  • Inicio >
  • Blog >
  • Encriptar e gardar contrasinais en base de datos

Encriptar e gardar contrasinais en base de datos

16 Feb 2011 por Luis

Comentarios: 28

Top secret

Sempre que se deseña unha aplicación que necesite identificación de usuarios suscítase a dúbida de se utilizar un sistema de autenticación externo (Facebook, Google , OpenId, ...) ou un sistema de autenticación propio. Cando se utiliza un sistema de autenticación propio, exponse o problema da seguridade á hora de gardar os contrasinais dos usuarios na nosa base de datos.

Aparentemente é un problema menor, xa que, á fin e ó cabo, o contrasinal dun usuario para o noso foro ou blog non parece unha información crítica. E isto sería así se non fose porque se sabe que moitos usuarios reutilizan os seus contrasinais en diferentes sitios. Se o unimos a que a conta de email é tamén un dato que se adoita pedir, calquera atacante que consiga roubar os contrasinais dun pequeno foro ou blog acabará cos contrasinais das contas de email, facebook, ... de moitos dos seus usuarios.

Ademais das medidas para evitar os ataques por forza bruta (pedir contrasinais cun número mínimo de carácteres, que conteñan maiúsculas, minúsculas, carácteres especiais e números, bloquear repetidos intentos de acceso errado, ...) e outros, hai que ter en conta a posibilidade de que se comprometa a nosa base de datos ou un backup da mesma.

Protexendo os contrasinais

Nunca debemos gardar os contrasinais dos usuarios sen cifrar. Isto é un erro máis común do que poida parecer xa que os clientes adoitan pedir como requisito o que se poida "recordar" o contrasinal ós usuarios (enviar por correo o contrasinal actual, non un novo).

Os contrasinais sempre han de gardarse na base de datos "cifrados" dalgún modo, de forma que o atacante non poida coñecelos. Se é requisito indispensable o que os contrasinais poidan ser "recordadas", deberán cifrarse cun algoritmo de "dobre sentido" para que poidan ser descifrados pola aplicación. Pero este sistema é moi pouco recomendable xa que o atacante poderá tamén descifralos. O mellor é cifrar os contrasinais cun algoritmo de "un só sentido" de forma que non se poidan descifrar.

Encriptando os contrasinais

En realidade, aínda que seguiremos chamándolle "encriptado" por comodidade, o que se adoita facer é pasar os contrasinais por unha función resumen como función resumen como MD5, SHA, ..., gardar na base de datos o "resumen" xerado e descartar o contrasinal.

$hash = sha1($password);
unset($password);
// Se $password == 'apple'
// $hash == 'd0be2dc421be4fcd0172e5afceea3970e2f3d940'

Máis adiante, para comprobar se un usuario puxo ben o seu contrasinal, o que faremos será encriptar o que o usuario nos envía e comparar o resultado do cifrado co que temos na nosa base de datos.

// $db_hash será o que temos gardado na base de datos
if ($db_hash === sha1($password))
{
	// Contrasinal correcto
}

Así, aínda que se comprometa a nosa base de datos, o atacante non poderá coñecer os contrasinais dos usuarios de forma fácil. O único que lle queda para poder descifralos é un ataque por forza bruta ou por táboas rainbow (táboas con cadeas de carácteres e o resultado de cifralas con algunhas funcións de resumo).

Elexir un algoritmo de cifrado

As funcións de resumo máis coñecidas e utilizadas son MD5 e SHA-1, pero dado que MD5 e SHA-1 foron comprometidas, actualmente estanse impondo novas funcións como son SHA-2 e Bcrypt.

Á hora de escoller unha delas para a nosa aplicación, deberemos ter en conta ademais da dispoñibilidade que teñamos, a velocidade de execución da función. Neste caso, ó contrario que na maioría dos casos en computación, a que máis nos interesa é a máis lenta (Bcrypt). A razón é que nós só imos cifrar un contrasinal ó identificarse un usuario. Non importa que este proceso leve 0.001 segundos ou 0.3 segundos. Con todo, para un atacante, esta diferenza fai que xerar táboas rainbow poida tardar dunhas horas a varios anos.

Engadindo sementes

Outra forma de porllo difícil ó atacante é engadir unha semente ó contrasinal antes de pasalo pola función resumen. Desta forma, aínda ten máis complicado utilizar as táboas rainbow, ó facer a cadea do contrasinal máis longa e máis improbable que estea na táboa rainbow.

Para porllo aínda peor, o mellor é que a semente sexa única para cada contrasinal, de forma que non poida facerse unha táboa rainbow coa túa semente incorporada, senón que tería que facerse unha táboa para cada contrasinal.

A forma de facelo sería así:

$salt = md5(uniqid(rand(), true)); // Ou ata mellor se tivese maiúsculas, minúsculas, carácteres especiais...
$hash = hash('sha512', $salt.$password); // Pode porse diante ou detrás, é igual
unset($password);
// Gardar en base de datos o $hash e $salt

Logo, cando queiramos autenticar un usuario:

// $db_salt será a semente que teremos en base de datos
// $db_hash será o resumo que temos gardado na base de datos
if ($db_hash === hash('sha512', $db_salt.$password))
{
	// Contrasinal correcto
}

Con este método, aínda que dous usuarios teñan o mesmo contrasinal, será imposible sabelo xa que o seu resumo será distinto.

Comentarios

28 comentarios. Comentar.

1. Danielo o 01 Xul 2011 ás 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 o 04 Xul 2011 ás 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 o 26 Ago 2011 ás 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 o 12 Xuñ 2012 ás 01:05:24

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

5. Anónimo o 20 Ago 2012 ás 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 o 21 Ago 2012 ás 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!!! o 29 Ago 2012 ás 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 o 29 Ago 2012 ás 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 o 09 Feb 2013 ás 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 o 09 Feb 2013 ás 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 o 06 Ago 2013 ás 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 o 06 Ago 2013 ás 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 o 07 Ago 2013 ás 17:23:19

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

saludos!!!!

14. MartinnitraM o 24 Nov 2014 ás 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

15. Luis o 16 Feb 2015 ás 13:10:54

Hola MartinnitraM.

Lo ideal sería no guardar la semilla con la contraseña, pero ¿cómo vas a guardar una semilla distinta para cada contraseña si no es en la BD?

Si utilizas sólo una semilla para todas las contraseñas lo mejor es que no esté en base de datos sino en un archivo de configuración o similar.

La idea de usar una semilla es que no se puedan usar tablas rainbow para descifrar las contraseñas. Si no usas semillas, el atacante puede usar la tabla rainbow "estándar". Si sólo usas una semilla y el atacante accede a ella, "sólo" tiene que generar una tabla rainbow añadiendo la semilla a cada cadena de caracteres. Si usas una semilla distinta para cada contraseña, tiene que generar una tabla rainbow para cada semilla.

16. Jose Antonio o 25 Feb 2015 ás 09:51:57

Alguien sabe de donde conseguir encriptar en sha2 con javascript¿?

17. aparicio o 25 Mai 2015 ás 19:57:40

Estoy en un curso y me piden que haga un encriptamiento básico de contraseña. Me pueden ayudar a como hacerlo

18. aparicio o 25 Mai 2015 ás 19:57:55

Estoy en un curso y me piden que haga un encriptamiento básico de contraseña. Me pueden ayudar a como hacerlo

19. Nely o 06 Xul 2015 ás 17:20:03

Muy buena página.

20. doug o 06 Ago 2015 ás 14:39:13

Hey hola, gracias por los consejos son muy buenos! Tengo un problema espero me pueden ayudar, estamos programando un modulo de login con mis compañeros de trabajo y notamos que esta instrucción

$pw = hash_hmac("sha512", $_POST['textboxContraseña'], "semilla");

nos da resultados distintos para la misma cadena, es mas a unos les genera una clave exageradamente larga de mas de 100 caracteres y a otros no, a que se puede deber esto? cual es el numero de caracteres que genera esta función?

21. Luis o 06 Ago 2015 ás 16:05:40

Respuesta:

http://stackoverflow.com/questions/18236106/what-is-the-l...

Normalmente debería darte una cadena de 128 caracteres (aunque veo que no esperabas tantos porque dices "exageradamente larga de mas de 100 caracteres"), aunque depende de la representación que pidas a la función (cuarto parámetro de hash_hmac) y de la configuración de tu servidor (session.hash_bits_per_character).

22. SEBASTIAN o 07 Out 2015 ás 06:07:40

disculpa soy principiante en php y quisera saber para que sirve el unset($password)

23. anónimo de Cali o 12 Out 2015 ás 22:39:08

Reciban mi saludo, la tecnología me atropella. Tengo un problema hace ya bastante, hay una persona que ha hackeado la cuenta de mi amigo y a su vez la mía en facebook, esta ya se esta tornado muy dificil no e como hacer para crearle una cuenta a el y encriptar la contraeña tanto de él como mía o si se puede encriptar las cuentas, les agradecería su ayuda. Dios les bendiga

24. Ariel Ganc o 29 Feb 2016 ás 19:53:29

Consulta ... existe la posiblilidad que nos campturen las contraseñas den elv iaje de cleinte al server? se pueden encriptar en el cleinte con js ajax o lo que fuera? gracias

25. ALFREDO o 13 Abr 2016 ás 01:42:41

¿CUÁL ES EL PROCEDIMIENTO Y REQUISITOS PARA IMPLEMENTAR UN SISTEMA DE ENCRIPTACIÓN EN UNA EMPRESA?.

26. ALFREDO o 13 Abr 2016 ás 01:57:31

¿CUÁL ES EL PROCEDIMIENTO Y REQUISITOS PARA IMPLEMENTAR UN SISTEMA DE ENCRIPTACIÓN EN UNA EMPRESA?.

estimado me pueden ayudar con esta información ya que estoy realizando un trabajo para un diplomado, saludos

27. Aida o 04 Ago 2016 ás 23:18:18

hola recien estoy comenzando a programar me pueden ayudar en donde pongo esas lineas de codigos

28. Dinh o 11 Abr 2017 ás 19:17:59

Solo vengo a decir que en PHP recomiendan no usar el SALT y dejar que se genere automáticamente por PHP ademas que recomiendas usar la función password_hash() en vez de crear uno mismo un mecanismo propio, ¿qué tan cierto es esto?.

Comentar

Comentar de forma anónima

Podes comentar poñendo calquera nome ou alcume, exceptuando os nomes de usuarios rexistrados. Máximo de 50 caracteres.

Comentar como usuario rexistrado

Rexistrarse. Esqueceches o teu contrasinal?