Introducción

Es una buena práctica (y algo que debemos hacer todos) cifrar las contraseñas si las vamos a guardar en una base de datos. También es importante hacerlo con un algoritmo de un solo camino; es decir, que se pueda cifrar pero que nunca se pueda hacer el proceso inverso.

¿Por qué cifrarlas, si mi base de datos está protegida?

Recordemos que la seguridad absoluta no existe y que siempre estamos propensos a un ataque de cualquier tipo, así que hay que proteger cada cosa individualmente.

Supongamos que nuestro servidor FTP sí está bien protegido, y que ningún usuario malicioso puede acceder a él. Ahora imaginemos que alguien logra hacer un ataque a la base de datos, y consigue todos los nombres de usuario y contraseñas. Lo que conseguirá serán contraseñas cifradas que no le servirán para nada. Pero si no las ciframos, podrán hacer lo que se les antoje.

Cifrando contraseñas

Debilidad de SHA1 y MD5

PHP ya viene con una opción para cifrar las contraseñas. No debemos usar nunca SHA1 ni MD5, porque aunque no son reversibles sí son predecibles con ataques de diccionario.

Aquí pongo un ejemplo:

Si yo uso SHA1 para cifrar “123” obtendré “40bd001563085fc35165329ea1ff5c5ecbdbbeef“. Esto nunca cambiará; siempre que yo cifre “123” obtendré el mismo resultado. Si alguien obtiene la contraseña cifrada que será “40bd001563085fc35165329ea1ff5c5ecbdbbeef”, no sabrá cuál contraseña plana es.

Pero comenzará a probar diferentes contraseñas y las irá cifrando, una por una. Puede comenzar con las más comunes, como 12345, 0000, etcétera. Por lo que conseguirá los siguientes resultados:

  • 12345 se convierte en 8cb2237d0679ca88db6464eac60da96345513964. No es esta contraseña porque no coincide con 40bd001563085fc35165329ea1ff5c5ecbdbbeef
  • 0000 se convierte en 39dfa55283318d31afe5a3ff4a0e3253e2045e43. Tampoco es esta porque no coincide con 40bd001563085fc35165329ea1ff5c5ecbdbbeef

E irá probando hasta que llegue a 123. Se verá así:

  • 123 se convierte en 40bd001563085fc35165329ea1ff5c5ecbdbbeef. Sí es, porque coincide con 40bd001563085fc35165329ea1ff5c5ecbdbbeef.

Lo peor de esto es que ya hay diccionarios online que tienen la mayoría de contraseñas más comunes y no tan comunes. Por eso nunca se debe usar esta función.

Con MD5 es lo mismo, así que no lo explicaré

Usando sal

Cuando usamos la función que PHP recomienda, se le añade sal a la contraseña cifrada. Es una cadena aleatoria que siempre producirá resultados diferentes.

La primera vez que cifre 123 puede que el resultado sea $2y$10$I4tJZFA.mxRSaxiy/uLUWOAB8CCoBI5UJUJZ/pkJ278oYJm6nstJ. La segunda $2y$10$uxHsXhUq90CzCbPNeoCL/enuYtJVmChl2Bc9biUJnq/LYtDDusk5K, la tercera $2y$10$8lVMtOjKf6nsorwQp4cWW.hzfuVqk.MyuKzesEPZu2r7fKt0X2V4K y así sucesivamente. Por lo que siempre dará un resultado distinto, y nunca se le podrá hacer un ataque como el que menciono arriba.

Algoritmos

Así que para guardar estas contraseñas el algoritmo es:

  1. Tener la contraseña plana del usuario
  2. Cifrarla con password_hash()
  3. Guardar el resultado del paso anterior (la contraseña ya cifrada) en la base de datos

Y para comprobar si esta contraseña es correcta cuando el usuario intente acceder es:

  1. Obtener contraseña cifrada de la base de datos
  2. Utilizar password_verify()
  3. Si el resultado del paso anterior es true, entonces la contraseña es correcta. De otro modo, no.

Ejemplo

Estructura de la tabla

El manual oficial recomienda que si vamos a usar password_hash declaremos nuestro campo en la base de datos con una longitud de 255.

Registrar usuario

Voy a abstraer y omitir  la conexión a la base de datos porque explicarlo no es el propósito de este post. Así que supondré que el usuario sabe manejar bases de datos y sus conexiones.

En el código de arriba tomamos el usuario y la contraseña del usuario, que pudieron ser enviadas por un formulario. Preparamos la consulta SQL para evitar inyecciones y guardamos.

Es importante notar que usamos password_hash() y le pasamos como primer argumento la contraseña plana, y como segundo una constante que es el algoritmo a ser utilizado. Recomiendo usar siempre esta constante.

Ese fue el proceso para guardar el usuario y la contraseña.

Comprobando credenciales

 

Ahora imaginemos que el usuario intenta acceder, y que igualmente manda el nombre de usuario y la contraseña por un formulario. El código quedaría así:

Conclusión

Así es como, a grosso modo, podemos comprobar si las credenciales de determinado usuario son válidas, al mismo tiempo que protegemos su contraseña.

Es muy importante mencionar que no debemos intentar crear nuestros propios algoritmos, por más que pensemos que somos listos nunca debemos de intentar hacerlo por nuestra cuenta.

No te vayas sin seguirme en Twitter, Facebook y GitHub, así me motivas a escribir más posts.
Igualmente te invito a suscribirte en la parte superior derecha escribiendo tu correo en el formulario.
Si tienes dudas déjalas en un comentario


parzibyte

He trabajado por más de 4 años en el desarrollo de software con experiencia en Java, PHP, JavaScript, HTML, Node.JS, Python, Android y Go. También he trabajado con bases de datos SQL como MySQL y SQLite, así como con bases de datos NoSQL usando MongoDB. Soy bueno utilizando algunos frameworks y herramientas como Firebase, jQuery, AngularJS, VueJS, CodeIgniter, Laravel, BulmaCSS, Bootstrap y Electron. Otros términos que conozco son: Arduino, GraphQL, API's, REST, AJAX, PouchDB, CouchDB, Experiencia de usuario, buenas prácticas de programación, Webpack, NPM, Administración de servidores y programación de scripts La plataforma en la que tengo más experiencia es la web, pero en mis ratos libres realizo unos pequeños ejercicios en C# y C. Estoy aquí para ayudarte a resolver tus problemas de programación y depuración :-)

2 Comments

Cuando descubrí que las contraseñas de una web no estaban hasheadas - Parzibyte's blog · noviembre 7, 2018 a las 2:04 pm

[…] es real, lo hice con password_hash de PHP, pero dudo que en el sitio usen PHP, deben usar algo […]

Hashear contraseñas con Python y bcrypt - Parzibyte's blog · diciembre 6, 2018 a las 4:46 pm

[…] algoritmo de bcrypt es usado también por PHP al hashear contraseñas. Igualmente está disponible en […]

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

This site uses Akismet to reduce spam. Learn how your comment data is processed.

A %d blogueros les gusta esto: