Node.JS

Encriptar contraseñas con Node

En este post de programación con JavaScript del lado del servidor en el entorno de Node te mostraré cómo asegurar las contraseñas de los usuarios; esto es, encriptarlas.

De hecho me parece que el término correcto es hashear, pues encriptar es convertir algo plano a encriptado y luego poder hacer lo contrario; en cambio hashear es convertir algo plano a encriptado, pero ya no poder obtener el valor original a partir del encriptado.

Como sea, te mostraré cómo asegurar, encriptar o cifrar las contraseñas. Vamos a usar el algoritmo bcrypt pues es perfecto para hashear contraseñas en Node.

Instalando paquete

Al inicio de todo, yo iba a usar el paquete bcrypt, pero el mismo depende de alguna implementación en C / C++ y hay que compilarlo (o si hay suerte, se te descarga uno compilado) entonces es un poco complejo, así que me decidí por usar bcryptjs que está escrito en JavaScript puro.

No te preocupes, tiene la misma seguridad y es el mismo algoritmo, la única diferencia es que la implementación en C / C++ es más rápida al encriptar contraseñas, pero hablamos de milisegundos. En fin, tú entiendes.

Así que finalmente usamos bcyptjs y la instalamos con:

npm install bcryptjs

Luego debemos importarla en donde la vayamos a usar, con:

const bcrypt = require("bcryptjs");

Nota: recuerda que siempre puedes visitar la documentación, yo te mostraré con ejemplos pero siempre puedes investigar más sobre el tema.

Encriptando contraseña

Cifrar contraseñas con Node y bcrypt

Vamos a ver cómo encriptar una contraseña. Esta contraseña es la que tenemos que guardar en la base de datos; nunca debemos guardar la versión en texto plano.

Para hashear la contraseña invocamos a bcrypt.hash que recibe el texto plano, el número de rondas para la sal y un callback que se ejecutará cuando la contraseña haya sido hasheada.

// Importamos paquete
const bcrypt = require("bcryptjs");
// Primero vamos a hashear la contraseña
const palabraSecretaTextoPlano = "hunter2";
// Entre más rondas, mejor protección, pero más consumo de recursos. 10 está bien
const rondasDeSal = 10;

bcrypt.hash(palabraSecretaTextoPlano, rondasDeSal, (err, palabraSecretaEncriptada) => {
 if (err) {
  console.log("Error hasheando:", err);
 } else {
  console.log("Y hasheada es: " + palabraSecretaEncriptada);
 }
});

Las rondas son importantes. Entre mayor rondas sean, más seguro será el hash contra ataques de fuerza bruta, pero más tiempo tomará nuestro servidor para encriptar así que debemos encontrar un balance perfecto.

Con 10 rondas está bien, pero si quieres puedes hacer pruebas con más rondas. Me parece que el límite es 31 por el momento.

Comprobando contraseñas

Como lo dije, no hay manera de desencriptar, solo de comprobar que el texto plano que proporciona el usuario (al loguearse por ejemplo) coincide con el hash. Recuerda que este hash debe venir de tu base de datos.

Para comprobar si la contraseña que proporciona el usuario coincide con la que tenemos guardada en la base de datos, invocamos al método compare. Este método recibe la contraseña en texto plano, el hash y una función callback.

// Importamos paquete
const bcrypt = require("bcryptjs");
// Este hash debe venir de tu base de datos, por ejemplo
// Nota: yo sé que este hash es "hunter2", obviamente es para ejemplificar
const palabraSecretaHasheada = "$2a$10$P9yvh9ew5ZueNRjQGX4Eiui9jNhaKJCX24mRsrWSNvj.0O2FjNSB2";
const palabraSecretaProporcionadaPorUsuario = "hunter2";
// Recuerda. Los argumentos son: texto plano, encriptada, y callback
bcrypt.compare(palabraSecretaProporcionadaPorUsuario, palabraSecretaHasheada, (err, coinciden) => {
 if (err) {
  console.log("Error comprobando:", err);
 } else {
  console.log("¿La contraseña coincide?: " + coinciden);
 }
});

Recuerda que eres libre de cambiar los valores para probar si coincide o no con otros valores.

Un vistazo con async

Si a ti no te gustan los callbacks y sabes cómo usar async y await, aquí te muestro un ejemplo de cómo hacer para tener un código más limpio. Al encriptar (recuerda que es un ejemplo mío, los valores pueden venir de donde quieras):

// Primero vamos a hashear la contraseña
const palabraSecretaTextoPlano = "hunter2";
// Entre más rondas, mejor protección, pero más consumo de recursos. 10 está bien
const rondasDeSal = 10;
const palabraSecretaEncriptada = await bcrypt.hash(palabraSecretaTextoPlano, rondasDeSal);

Y al comprobar:

// Recuperamos la contraseña de la petición
const palabraSecretaTextoPlano = "hunter2";
// Y la guardada en la base de datos
const palabraSecretaEncriptada = "$2a$10$P9yvh9ew5ZueNRjQGX4Eiui9jNhaKJCX24mRsrWSNvj.0O2FjNSB2";
// Comprobamos...
const palabraSecretaValida = await bcrypt.compare(palabraSecretaTextoPlano, palabraSecretaEncriptada);

De este modo nos evitamos el “infierno de callbacks”, tenemos un código más ordenado y legible.

Estoy aquí para ayudarte 🤝💻


Estoy aquí para ayudarte en todo lo que necesites. Si requieres alguna modificación en lo presentado en este post, deseas asistencia con tu tarea, proyecto o precisas desarrollar un software a medida, no dudes en contactarme. Estoy comprometido a brindarte el apoyo necesario para que logres tus objetivos. Mi correo es parzibyte(arroba)gmail.com, estoy como@parzibyte en Telegram o en mi página de contacto

No te pierdas ninguno de mis posts 🚀🔔

Suscríbete a mi canal de Telegram para recibir una notificación cuando escriba un nuevo tutorial de programación.
parzibyte

Programador freelancer listo para trabajar contigo. Aplicaciones web, móviles y de escritorio. PHP, Java, Go, Python, JavaScript, Kotlin y más :) https://parzibyte.me/blog/software-creado-por-parzibyte/

Entradas recientes

Desplegar PWA creada con Vue 3, Vite y SQLite3 en Apache

Ya te enseñé cómo convertir una aplicación web de Vue 3 en una PWA. Al…

6 días hace

Arquitectura para wasm con Go, Vue 3, Pinia y Vite

En este artículo voy a documentar la arquitectura que yo utilizo al trabajar con WebAssembly…

6 días hace

Vue 3 y Vite: crear PWA (Progressive Web App)

En un artículo anterior te enseñé a crear un PWA. Al final, cualquier aplicación que…

6 días hace

Errores de Comlink y algunas soluciones

Al usar Comlink para trabajar con los workers usando JavaScript me han aparecido algunos errores…

6 días hace

Esperar promesa para inicializar Store de Pinia con Vue 3

En este artículo te voy a enseñar cómo usar un "top level await" esperando a…

6 días hace

Solución: Apache – Server unable to read htaccess file

Ayer estaba editando unos archivos que son servidos con el servidor Apache y al visitarlos…

7 días hace

Esta web usa cookies.