Introducción
Proteger las contraseñas de los sitios web es una habilidad esencial que cualquier desarrollador debería tener. JavaScript
ofrece una opción para garantizar el almacenamiento seguro y el procesamiento de contraseñas u otros datos sensibles utilizando los algoritmos de hash proporcionados por el módulo BcryptJS
de JavaScript
.
En este tutorial, aprenderás sobre BcryptJS
y el hashing para configurar un servidor básico de express que almacenará contraseñas como hashes en una base de datos en lugar de cadenas sin procesar, y las recuperará para autenticar la contraseña.
Prerrequisitos
Para continuar con este tutorial, debes tener la siguiente configuración.
-
Una versión estable de Node.js instalada en tu computadora con la versión 12.x o superior. Puedes usar este tutorial de DigitalOcean para instalar la última versión de Node Js en tu computadora.
-
Debes saber cómo programar en JavaScript.
-
Debes tener Express JS instalado en tu computadora. Puedes usar esta guía para aprender cómo configurar un servidor Express.
-
Por último, necesitarás la base de datos de la comunidad MongoDB o Atlas para completar este tutorial. Puedes instalarla utilizando una de estas guías de DigitalOcean sobre cómo instalar MongoDB.
¿Por qué usar BcryptJS?
Bcrypt es un algoritmo de hash para crear hashes de contraseñas y almacenarlas en caso de una violación de datos. Este algoritmo de hash avanzado utiliza sales, lo que hace difícil de descifrar mediante ataques como el de fuerza bruta.
BcryptJS es la implementación en JavaScript del algoritmo de hash Bcrypt, lo que te permite utilizar el cifrado de hash sin tener que lidiar con funciones de hash complejas. Algunas de las razones que hacen de BcryptJS una gran elección para la seguridad de contraseñas son las siguientes:
-
Seguridad – BcryptJS implementa el algoritmo Bcrypt, un algoritmo lento (lo cual es bueno en el hashing) y que requiere una gran potencia computacional. Esto dificulta la tarea de los atacantes de descifrar el hash de la contraseña, asegurando la seguridad de las contraseñas incluso en caso de una violación de datos._
-
Saltado – BcryptJS maneja la generación de sales aleatorias para contraseñas para garantizar la seguridad del almacenamiento (aprenderemos más sobre los hashes y las sales en la próxima sección). Las sales hacen que el hash de una contraseña relativamente débil sea más complejo, lo que dificulta su descifrado.
-
Facilidad de uso – BcryptJS proporciona a los desarrolladores de JavaScript una herramienta para cifrar sus contraseñas sin requerir un profundo conocimiento de hashing._
En el siguiente paso, aprenderemos brevemente sobre hashes y saltos.
¿Cómo funciona el hash?
Antes de utilizar estos conceptos en tus proyectos, debes entender cómo funciona el hash y el salto.
Hashing
El hashing es la conversión de una cadena simple o texto plano en una cadena de caracteres aleatorios (cifrado). Esto permite almacenar y/o transmitir datos sensibles de forma segura. El hashing involucra los siguientes pasos clave:
Entrada de datos – En primer lugar, se almacena el dato que puede ser de cualquier tipo (binario, caracter, decimal, etc.) como texto plano o cadena.
La función de hash – La función de hash es un algoritmo matemático que toma la entrada de los datos y la convierte en un conjunto de caracteres o códigos de hash. Las funciones de hash son deterministas (producen la misma salida para la misma entrada) y funciones unidireccionales (esto significa que es casi imposible invertir el resultado de las funciones de hash, es decir, un hash en sus datos de entrada).
Resistencia a colisiones – Esto significa que una función hash se crea teniendo en cuenta la idea de resistencia a colisiones, es decir, dos entradas diferentes no pueden tener la misma salida (código hash).
Autenticación – Las funciones hash son deterministas, produciendo el mismo hash para la misma entrada. Por lo tanto, al autenticar una contraseña almacenada como hash, la idea general es que si la contraseña para autenticar coincide con el hash almacenado en la base de datos, la contraseña es correcta.
Salado
Dado que el hash ha existido durante décadas, ha habido desarrollos como tablas arcoíris, que contienen miles de millones de entradas de datos que contienen cadenas de datos y sus hash respectivos basados en diferentes algoritmos de hash.
Ahora, consideremos una situación en la que un usuario que crea una cuenta en tu sitio web crea una contraseña débil. Por lo tanto, en caso de una violación de datos, un atacante puede buscar los hashes de tus usuarios y encontrar la coincidencia del hash para la cuenta de usuario con una contraseña débil. Esto sería desastroso en aplicaciones de alta seguridad. Para evitar que esto suceda, se utilizan los saltos.
El salado es una capa adicional de seguridad agregada a los hashes al agregar una cadena aleatoria de caracteres al hash de una contraseña antes de almacenarla en una base de datos. Por lo tanto, incluso si los datos se filtran en una violación, será difícil para un atacante descifrar un hash que contiene sal. Considera el siguiente ejemplo:
Como podemos ver claramente, la contraseña almacenada con sal es menos probable que se pueda descifrar debido a la naturaleza determinista de los hashes. Por lo tanto, si un atacante, por ejemplo, busca esta cadena de hash+contraseña en una tabla arcoíris, no obtendrá la contraseña real sino algo completamente diferente.
Ahora estás listo para usar BcryptJS y asegurar tus contraseñas de una manera estándar en la industria.
Instalación de BcryptJS y otros módulos requeridos
Ahora que conoces sobre el hash y la sal, solo te queda encender tu computadora y comenzar a programar. La estructura del proyecto será la siguiente:

Primero, vamos a crear un proyecto npm con los siguientes pasos:
-
Abre una carpeta y crea un archivo llamado
app.js.
-
Abre una ventana de terminal en esta carpeta y escribe el siguiente comando
Después de esto, se te pedirán entradas, pero puedes presionar Enter sin proporcionar ninguna entrada.
- Luego, crea 3 archivos más, a saber,
auth.js
db.js
User.js
- En la misma ventana de terminal, escribe el siguiente comando para instalar los paquetes necesarios.
Ahora tienes un entorno de proyecto completo para seguir este tutorial. En el siguiente paso, aprenderás cómo crear un servidor para utilizar BcryptJS y almacenar y autenticar contraseñas de forma segura con MongoDB.
Configuración de un servidor con Express JS
Ahora que has configurado la estructura del proyecto, puedes crear un servidor que utilice bcrytjs
para asegurar las contraseñas almacenándolas como hashes y autenticándolas. Crearemos el servidor siguiendo los siguientes pasos adecuados.
Paso 1: Crear una conexión a la base de datos de MongoDB
Para conectarnos a mongoDB
, estamos utilizando la edición comunitaria. Para mantener el proyecto organizado, guardarás el código para configurar la conexión en el archivo db.js
.
Aquí, importas el paquete mongoose
, que proporciona una API para conectar JavaScript a MongoDB.
Además, en este caso, la URI de conexión es para una instalación local de mongoDB. Si estás utilizando una base de datos en la nube (como Atlas), solo necesitas cambiar la URI por la URI específica de tu base de datos.
Información
Una URI es similar a una URL de servidor con la diferencia de que una URI puede identificar el nombre y la identidad de los recursos y su ubicación en Internet. En cambio, una URL es un subconjunto de una URI que solo puede hacer lo último.
La función connectToMongo
es una función async porque mongoose.connect
devuelve una promesa de JavaScript. Esta función dará una conexión al finalizar la ejecución con éxito. De lo contrario, devolverá un error.
Finalmente, utilizamos module.exports
para exportar esta función siempre que el módulo db.js
sea importado.
Paso 2 – Creando un esquema de usuario
Necesitarás un esquema básico para crear o autenticar usuarios en una base de datos MongoDB. Si no sabes qué es un esquema, puedes utilizar esta excelente guía de DO para entender y crear el esquema en MongoDB. Solo utilizaremos dos campos para nuestro esquema, email
y password
.
Utiliza el siguiente código en tu módulo User.js
.
Creamos dos campos en este esquema, email
y password
. Ambos son campos obligatorios y de tipo de dato string. Además, email
es un campo único, lo que significa que un correo electrónico solo se puede usar una vez para tener una cuenta en este servidor.
Finalmente, exportamos un modelo llamado users
. Un modelo representa un esquema en la base de datos. Puedes pensar en un esquema como una regla para definir un modelo, mientras que el modelo se almacena como una colección en la base de datos MongoDB
.
Puedes convertir un esquema en un modelo utilizando la función model() de la biblioteca mongoose
.
Paso 3 – Configurando el servidor en app.js
Después de seguir los pasos anteriores, has creado con éxito un modelo y un módulo para establecer una conexión con la base de datos MongoDB
. Ahora aprenderás a configurar el servidor. Utiliza el siguiente código en tu archivo app.js
.
Aquí, importamos los módulos express
y db.js
para conectarnos a MongoDB. Luego, utilizamos el middleware express.json()
para manejar las respuestas en formato JSON. Las rutas se crean en un módulo diferente (auth.js
) para mantener el código limpio y organizado. Finalmente, creamos un punto final para que el servidor escuche en el puerto 3300
en localhost. (Puedes usar cualquier puerto de tu elección)
Cifrando contraseñas y almacenándolas en una base de datos MongoDB
Hasta este punto, el servidor está casi listo, y ahora crearás endpoints para el servidor. Crearemos dos endpoints: signup
y login
. En el endpoint de signup
, tomaremos el correo electrónico y la contraseña de un nuevo usuario y la almacenaremos con cifrado para la contraseña usando BcryptJS.
En el archivo auth.js
, escribe el siguiente código:
Hacemos las importaciones necesarias y luego configuramos el enrutador de express para crear el endpoint /signup
. Estamos utilizando el método POST
para que las credenciales no se revelen en la URL de la aplicación. Después de eso, creamos una sal usando la función genSalt
del paquete scripts
; el parámetro pasado a la función genSalt() contiene la longitud de los caracteres de la sal. Luego, utilizamos la función hash() de BcryptJS, que toma un parámetro requerido, la cadena de contraseña que se convertirá en código hash, y un argumento opcional, la cadena de sal. Y luego devuelve un hash que contiene tanto la contraseña
como la sal
.
Después de esto, usamos la función create() del módulo mongoose
para crear un documento en nuestra base de datos definido por las reglas del modelo users. Recibe un objeto Javascript
que contiene el correo electrónico y la contraseña, pero en lugar de proporcionar una cadena sin procesar, le damos el secPass (hash de la contraseña + sal) que se almacenará en la base de datos. De esta manera, hemos almacenado de forma segura una contraseña en la base de datos utilizando su hash combinado con una sal en lugar de una cadena sin procesar. Finalmente, devolvemos una respuesta JSON
que contiene el modelo de usuario. (Este método de enviar respuestas solo se utiliza en la fase de desarrollo; en producción, se reemplazará por un token de autenticación u otra cosa).
Para probar este punto final, primero debes ejecutar el servidor, lo cual se puede hacer escribiendo el siguiente comando en la terminal.
Este comando ejecutará tu servidor en localhost y en el puerto 3300
(o en el puerto que especifiques). Luego, puedes enviar una solicitud HTTP
a la URL http://localhost:3300/auth/signup
con el siguiente cuerpo:
Esto producirá la siguiente salida/respuesta:
Nota: El hash de la contraseña y el ID no serán iguales, ya que siempre son únicos.
En la siguiente sección, aprenderás cómo acceder al hash almacenado para la contraseña y autenticarlo con una contraseña proporcionada en el inicio de sesión/autenticación.
Accediendo a la contraseña encriptada y usándola para autenticación
Hasta ahora, has aprendido sobre BcryptJS,
hashing,
salting,
y desarrollar un servidor express
que crea nuevos usuarios con contraseñas almacenadas como hashes. Ahora, aprenderás cómo usar la contraseña almacenada y autenticar a un usuario cuando intenta iniciar sesión en la aplicación.
Para agregar un método de autenticación, agrega la siguiente ruta en tu archivo auth.js
después de la ruta /signup
:
Aquí, usamos la subruta /auth/login
para realizar una autenticación de inicio de sesión para un usuario que ya existe. Al igual que el punto de conexión /auth/signup
, esta será una función async-await ya que BcryptJS devuelve promesas.
En primer lugar, usamos la función findOne
de la biblioteca Mongoose, que se utiliza para encontrar un documento en una colección basado en una consulta de búsqueda dada. En este caso, estamos buscando al usuario basado en el correo electrónico. Si no hay un usuario con el correo electrónico proporcionado, esto enviará una respuesta con el código de estado 400 para credenciales inválidas. (No es una buena práctica proporcionar qué parámetro es incorrecto durante el inicio de sesión, ya que los atacantes pueden usar esa información para encontrar cuentas existentes).
Si el usuario con el correo electrónico proporcionado existe, el programa pasa a la comparación de contraseñas. Para este propósito, BcryptJS
proporciona el método compare()
, que toma una cadena sin formato como primer argumento y un hash
(con o sin salt
) como segundo argumento. Luego, devuelve una promesa Booleana
; true
si la contraseña coincide con el hash y false
si no coinciden. Luego, puedes agregar una simple comprobación utilizando una declaración if
y devolver éxito o error en función de la comparación.
Finalmente, exportarás el router de express
utilizando module.exports para que el punto de inicio app.js
lo use para las rutas.
Para probar esta ruta, puedes enviar otra respuesta HTTP
a esta URL http://localhost:3300/auth/login
con el cuerpo de la siguiente manera:
Esta solicitud dará la siguiente respuesta:
Finalmente, auth.js
se verá así:
Conclusión
Este tutorial creó un servidor para explicar el uso de BcryptJS para almacenar y acceder de manera segura a contraseñas de bases de datos. Puedes llevar esto más lejos:
-
Implementando más rutas para otras tareas como obtener credenciales de usuario, actualizar credenciales de usuario, etc.
-
Implementando tokens de autenticación para enviar como respuestas, etc.
Esto comienza tu camino para manejar contraseñas de forma segura; siempre puedes añadir más seguridad con diferentes técnicas y tecnologías, permitiéndote crear aplicaciones más seguras y resilientes.