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.
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.
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.
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.
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.
Ya te enseñé cómo convertir una aplicación web de Vue 3 en una PWA. Al…
En este artículo voy a documentar la arquitectura que yo utilizo al trabajar con WebAssembly…
En un artículo anterior te enseñé a crear un PWA. Al final, cualquier aplicación que…
Al usar Comlink para trabajar con los workers usando JavaScript me han aparecido algunos errores…
En este artículo te voy a enseñar cómo usar un "top level await" esperando a…
Ayer estaba editando unos archivos que son servidos con el servidor Apache y al visitarlos…
Esta web usa cookies.