node.js - nodejs - password hash and salt generator
ContraseƱa SALT y HASH en nodejs w/crypto (7)
Almacene la contraseña y la sal en columnas separadas en su base de datos, o (mi método preferido), almacene sus contraseñas en su base de datos en un formato compatible con RFC 2307, sección 5.3. Un ejemplo sería {X-PBKDF2}base64salt:base64digest
. También puede almacenar su recuento de iteraciones allí, lo que le permite aumentar el recuento de iteraciones en el futuro para nuevas cuentas y cuentas que actualicen sus contraseñas, sin interrumpir el inicio de sesión para todos los demás.
Un ejemplo de hash de mi propio módulo PBKDF2 para Perl parece
{X-PBKDF2}HMACSHA1:AAAD6A:8ODUPA==:1HSdSVVwlWSZhbPGO7GIZ4iUbrk=
que incluye el algoritmo hash específico utilizado, así como el número de iteraciones, la sal y la clave resultante.
Estoy tratando de averiguar cómo eliminar y codificar una contraseña en nodejs usando el módulo criptográfico. Puedo crear la contraseña con hash haciendo esto:
UserSchema.pre(''save'', function(next) {
var user = this;
var salt = crypto.randomBytes(128).toString(''base64'');
crypto.pbkdf2(user.password, salt, 10000, 512, function(err, derivedKey) {
user.password = derivedKey;
next();
});
});
Sin embargo, estoy confundido acerca de cómo validar más tarde la contraseña.
UserSchema.methods.validPassword = function(password) {
// need to salt and hash this password I think to compare
// how to I get the salt?
}
Ante la misma pregunta, reuní todo en un solo módulo: https://www.npmjs.org/package/password-hash-and-salt
Utiliza pbkdf2 y almacena hash, sal, algoritmo e iteraciones en un solo campo. Espero eso ayude.
Creo que este tutorial sería el más adecuado para ti. Solo ve a través de esto, es lo mejor que he encontrado hasta ahora. Tutorial de pasaporte con Node.js y Crypto
Espero que te sea útil.
En cualquier mecanismo de persistencia (base de datos) que esté utilizando, almacenaría el hash resultante junto con la sal y el número de iteraciones, ambas de las cuales serían de texto simple. Si cada contraseña tiene una sal diferente (lo que debe hacer), debe guardar esa información.
Luego, comparará la nueva contraseña de texto sin formato, hash que usa la misma sal (e iteraciones) y luego comparará la secuencia de bytes con la almacenada.
Para generar la contraseña (pseudo)
function hashPassword(password) {
var salt = crypto.randomBytes(128).toString(''base64'');
var iterations = 10000;
var hash = pbkdf2(password, salt, iterations);
return {
salt: salt,
hash: hash,
iterations: iterations
};
}
Para validar la contraseña (pseudo)
function isPasswordCorrect(savedHash, savedSalt, savedIterations, passwordAttempt) {
return savedHash == pbkdf2(passwordAttempt, savedSalt, savedIterations);
}
Esta es una versión modificada de la respuesta de @Matthews, usando TypeScript
import * as crypto from ''crypto'';
const PASSWORD_LENGTH = 256;
const SALT_LENGTH = 64;
const ITERATIONS = 10000;
const DIGEST = ''sha256'';
const BYTE_TO_STRING_ENCODING = ''hex''; // this could be base64, for instance
/**
* The information about the password that is stored in the database
*/
interface PersistedPassword {
salt: string;
hash: string;
iterations: number;
}
/**
* Generates a PersistedPassword given the password provided by the user. This should be called when creating a user
* or redefining the password
*/
export async function generateHashPassword(password: string): Promise<PersistedPassword> {
return new Promise<PersistedPassword>((accept, reject) => {
const salt = crypto.randomBytes(SALT_LENGTH).toString(BYTE_TO_STRING_ENCODING);
crypto.pbkdf2(password, salt, ITERATIONS, PASSWORD_LENGTH, DIGEST, (error, hash) => {
if (error) {
reject(error);
} else {
accept({
salt,
hash: hash.toString(BYTE_TO_STRING_ENCODING),
iterations: ITERATIONS,
});
}
});
});
}
/**
* Verifies the attempted password against the password information saved in the database. This should be called when
* the user tries to log in.
*/
export async function verifyPassword(persistedPassword: PersistedPassword, passwordAttempt: string): Promise<boolean> {
return new Promise<boolean>((accept, reject) => {
crypto.pbkdf2(passwordAttempt, persistedPassword.salt, persistedPassword.iterations, PASSWORD_LENGTH, DIGEST, (error, hash) => {
if (error) {
reject(error);
} else {
accept(persistedPassword.hash === hash.toString(BYTE_TO_STRING_ENCODING));
}
});
});
}
Hay dos pasos principales involucrados en este escenario
1) Creando y almacenando contraseña
Aquí tendrás que hacer lo siguiente.
- Toma la contraseña de usuario
- Generar una cadena de caracteres aleatorios (sal)
- Combina la sal con la contraseña introducida por el usuario.
- Hash la cadena combinada.
- Almacena el hash y la sal en la base de datos.
2) Validando contraseña de usuario
Este paso sería necesario para autenticar al usuario.
El usuario ingresará el nombre de usuario / correo electrónico y la contraseña.
Recupera el hash y la sal según el nombre de usuario ingresado
Combina la sal con la contraseña de usuario.
Hash la combinación con el mismo algoritmo hash.
Compara el resultado.
Este tutorial tiene una explicación detallada sobre cómo hacerlo con nodejs crypto. Exactamente lo que buscas. Contraseñas de Salt Hash usando NodeJS crypto
Según la documentación de nodejs ( http://nodejs.org/api/crypto.html ), no parece que exista un método específico que validará una contraseña para usted. Para validarlo manualmente, deberá calcular el hash de la contraseña proporcionada actualmente y compararla con la almacenada para la igualdad. Básicamente, harás lo mismo con la contraseña de desafío que hiciste con el original, pero usarás la sal almacenada en la base de datos en lugar de generar una nueva, y luego compararás los dos hashes.
Si no está demasiado comprometido con el uso de la biblioteca criptográfica incorporada, le recomendamos que utilice bcrypt lugar. Los dos son casi iguales en el frente de seguridad, pero creo que bcrypt tiene una interfaz más fácil de usar. Un ejemplo de cómo usarlo (tomado directamente de los documentos de bcrypt en la página enlazada arriba) sería este:
Crear un hash:
var bcrypt = require(''bcrypt'');
var salt = bcrypt.genSaltSync(10);
var hash = bcrypt.hashSync("B4c0///", salt);
// Store hash in your password DB.
Para comprobar una contraseña:
// Load hash from your password DB.
bcrypt.compareSync("B4c0///", hash); // true
bcrypt.compareSync("not_bacon", hash); // false
Editar para añadir:
Otra ventaja de bcrypt es que la salida de la función genSalt contiene tanto el hash como la sal en una cadena . Esto significa que puede almacenar solo el elemento en su base de datos, en lugar de dos. También se proporciona un método que generará una sal al mismo tiempo que se produce el hash, por lo que no tiene que preocuparse por administrar la sal en absoluto.
Editar para actualizar:
En respuesta al comentario de Peter Lyons: estás 100% correcto. Yo había asumido que el módulo bcrypt que había recomendado era una implementación de javascript y, por lo tanto, usarlo de forma asíncrona realmente no aceleraría las cosas en el modelo de un solo hilo del nodo. Resulta que este no es el caso; el módulo bcrypt utiliza código nativo de c ++ para sus cálculos y se ejecutará de forma asíncrona más rápido. Peter Lyons tiene razón, primero debe usar la versión asíncrona del método y solo seleccionar la síncrona cuando sea necesario. El método asíncrono puede ser tan lento como el síncrono, pero el síncrono siempre será lento.