J'essaye de comprendre comment saler et hacher un mot de passe dans nodejs en utilisant le module de cryptographie. Je suis capable de créer le mot de passe haché en faisant ceci:
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();
});
});
Cependant, je ne comprends pas comment valider plus tard le mot de passe.
UserSchema.methods.validPassword = function(password) {
// need to salt and hash this password I think to compare
// how to I get the salt?
}
Quel que soit le mécanisme de persistance (base de données) utilisé, vous stockez le hachage résultant à côté du sel et du nombre d'itérations, qui sont tous deux du texte en clair. Si chaque mot de passe utilise un sel différent (ce que vous devez faire), vous devez également enregistrer ces informations.
Vous pouvez alors comparer le nouveau mot de passe en texte brut, hacher celui-ci en utilisant le même sel (et les mêmes itérations), puis comparer la séquence d'octets à celle stockée.
Pour générer le mot de passe (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
};
}
Pour valider le mot de passe (pseudo)
function isPasswordCorrect(savedHash, savedSalt, savedIterations, passwordAttempt) {
return savedHash == pbkdf2(passwordAttempt, savedSalt, savedIterations);
}
D'après la documentation de nodejs ( http://nodejs.org/api/crypto.html ), il ne semble pas qu'il existe une méthode spécifique permettant de valider un mot de passe. Pour le valider manuellement, vous devez calculer le hachage du mot de passe actuellement fourni et le comparer à celui stocké pour égalité. Fondamentalement, vous ferez la même chose avec le mot de passe de challenge que vous avez utilisé avec l'original, mais utilisez le sel stocké dans la base de données au lieu d'en générer un nouveau, puis comparez les deux hachages.
Si vous n'êtes pas trop déterminé à utiliser la bibliothèque de chiffrement intégrée, je vous recommanderais plutôt d'utiliser bcrypt . Les deux sont à peu près égaux sur le plan de la sécurité, mais je pense que bcrypt a une interface plus conviviale. Voici un exemple d'utilisation (tiré directement des documents bcrypt de la page liée ci-dessus):
Créer un hachage:
var bcrypt = require('bcrypt');
var salt = bcrypt.genSaltSync(10);
var hash = bcrypt.hashSync("B4c0/\/", salt);
// Store hash in your password DB.
Pour vérifier un mot de passe:
// Load hash from your password DB.
bcrypt.compareSync("B4c0/\/", hash); // true
bcrypt.compareSync("not_bacon", hash); // false
Modifier pour ajouter:
Un autre avantage de bcrypt est que la sortie de la fonction genSalt contient à la fois le hachage et le sel dans une chaîne . Cela signifie que vous ne pouvez stocker qu'un seul élément dans votre base de données, au lieu de deux. Il existe également une méthode fournie qui génère un sel en même temps que le hachage afin que vous n'ayez pas à vous soucier de la gestion du sel.
Modifier pour mettre à jour:
En réponse au commentaire de Peter Lyons: vous avez 100% raison. J'avais supposé que le module bcrypt que j'avais recommandé était une implémentation javascript. Par conséquent, son utilisation de manière asynchrone n'accélérerait pas vraiment les choses sur le modèle à un seul thread du noeud. Il s'avère que ce n'est pas le cas. le module bcrypt utilise du code natif c ++ pour ses calculs et s'exécutera plus rapidement de manière asynchrone. Peter Lyons a raison, vous devez d’abord utiliser la version asynchrone de la méthode et ne choisir que la version synchrone lorsque cela est nécessaire. La méthode asynchrone pourrait être aussi lente que la méthode synchrone, mais la méthode synchrone sera toujours être lente.
Stockez le mot de passe et le sel dans des colonnes distinctes de votre base de données, ou (ma méthode préférée), stockez vos mots de passe dans votre base de données dans un format compatible avec RFC 2307 section 5.3. Un exemple serait {X-PBKDF2}base64salt:base64digest
. Vous pouvez également y stocker votre nombre d'itérations, ce qui vous permet d'augmenter le nombre d'itérations à l'avenir pour les nouveaux comptes et les comptes qui mettent à jour vos mots de passe, sans interrompre les connexions pour tous les autres.
Un exemple de hachage de mon propre module PBKDF2 pour Perl ressemble à{X-PBKDF2}HMACSHA1:AAAD6A:8ODUPA==:1HSdSVVwlWSZhbPGO7GIZ4iUbrk=
qui inclut l'algorithme de hachage spécifique utilisé, ainsi que le nombre d'itérations, le sel et la clé résultante.
Face à la même question, j'ai tout rassemblé dans un seul module: https://www.npmjs.org/package/password-hash-and-salt
Il utilise pbkdf2 et stocke le hachage, le sel, l’algorithme et les itérations dans un seul champ. J'espère que ça aide.
Je pense que ce tutoriel serait le plus approprié pour vous. Il suffit de le parcourir, c’est le meilleur que j’ai trouvé à ce jour . Tutoriel Passport avec Node.js et Crypto
J'espère que vous le trouverez utile.
Ceci est une version modifiée de @Matthews answer, en utilisant 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));
}
});
});
}
Ce scénario comporte deux étapes principales.
1) Création et stockage du mot de passe
Ici, vous devrez faire ce qui suit.
2) Validation du mot de passe de l'utilisateur
Cette étape serait nécessaire pour authentifier l'utilisateur.
L'utilisateur entrera le nom d'utilisateur/email et le mot de passe.
Récupérez le hash et le sel en fonction du nom d'utilisateur entré
Combinez le sel avec le mot de passe de l'utilisateur
Hash la combinaison avec le même algorithme de hachage.
Comparez le résultat.
Ce tutoriel a une explication détaillée sur la façon de le faire avec nodejs crypto. Exactement ce que vous cherchez. Mots de passe Salt Hash utilisant NodeJS crypto