Mot de passe SALT et HASH dans nodejs w / crypto

j'essaie de trouver comment saler et hacher un mot de passe dans nodejs en utilisant le module crypto. Je suis capable de créer le mot de passe hashé 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 suis confus sur la façon de 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?
}
23
demandé sur gevorg 2013-06-20 01:13:20

7 réponses

quel que soit le mécanisme de persistance (base de données) que vous utilisez, vous stockerez le hachage résultant à côté du sel et du nombre d'itérations, les deux étant en clair. Si chaque mot de passe a un sel différent (ce que vous devriez faire), vous devez sauvegarder cette information.

vous comparez alors le nouveau mot de passe en clair, hachez qu'en utilisant le même sel (et les itérations), puis comparez la séquence d'octets avec celui stocké.

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);
}
33
répondu Matthew 2017-01-06 15:38:04

basé sur la documentation de nodejs ( http://nodejs.org/api/crypto.html ), il ne semble pas qu'il y ait une méthode spécifique pour valider un mot de passe pour vous. Pour le valider manuellement, vous devrez calculer le hachage du mot de passe actuellement fourni et le comparer à celui stocké pour l'égalité. Fondamentalement, vous ferez la même chose avec le mot de passe de défi que vous avez fait avec l'original, mais utilisez le sel stocké dans la base de données au lieu de générer un nouveau, et puis comparer les deux coups.

si vous n'êtes pas trop engagé à utiliser la bibliothèque crypto intégrée, je vous recommande d'utiliser bcrypt à la place. 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 sur 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.

à 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 pouvez stocker seulement l'article unique dans votre base de données, au lieu de deux. Il y a aussi une méthode qui permet de générer un sel en même temps que le malaxage, de sorte que vous n'avez 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, et donc l'utiliser de manière asynchrone n'accélérerait pas vraiment les choses sur le modèle fileté unique de node. Il s'avère que ce n'est pas le cas; le module bcrypt utilise du code C++ natif pour ses calculs et tournera plus rapidement de manière asynchrone. Peter Lyons a raison. devez utiliser la version asynchrone de la méthode premier et seulement choisir le synchrone si nécessaire. La méthode asynchrone pourrait être aussi lent que le synchrone, mais synchrone, on va toujours être lent.

23
répondu TwentyMiles 2017-05-23 11:33:24

soit stocker le mot de passe et le sel dans des colonnes séparées dans votre base de données, ou (ma méthode préférée), stocker vos mots de passe dans votre base de données dans un format qui est 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 dans le futur pour les nouveaux comptes et les comptes qui mettent à jour vos mots de passe, sans casser les logins pour tout le monde.

un exemple de hachage de mon propre module PBKDF2 pour Perl ressemble à

{X-PBKDF2}HMACSHA1:AAAD6A:8ODUPA==:1HSdSVVwlWSZhbPGO7GIZ4iUbrk= qui comprend l'algorithme de hachage spécifique utilisé, ainsi que le nombre d'itérations, le sel, et la clé résultante.

7
répondu hobbs 2013-06-19 21:25:18

je pense que ce tutoriel serait le plus convenable pour vous. Allez simplement à travers elle, c'est le meilleur que j'ai trouvé encore. tutoriel passeport avec noeud.JS et Crypto

J'espère que vous le trouverez utile.

5
répondu Saransh Mohapatra 2013-06-21 18:55:05

face à la même question, j'ai réuni tout en un seul module: https://www.npmjs.org/package/password-hash-and-salt

il utilise pbkdf2 et stocke le hash, le sel, l'algorithme et les itérations dans un seul champ. Espérons que cela aide.

5
répondu florian 2014-05-02 20:42:04

il y a deux étapes importantes dans ce scénario

1) Création et stockage du mot de passe

ici, vous devrez faire ce qui suit.

  • Prendre le mot de passe utilisateur
  • générer une chaîne de caractères aléatoires (sel)
  • combiner le sel avec le mot de passe saisi par l'utilisateur
  • hachez la chaîne combinée.
  • Stocker le hash et le sel dans la base de données.

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/courriel et le mot de passe.

  • Récupérer le hash et le sel selon le nom d'utilisateur entré

  • combiner le sel avec le mot de passe de l'utilisateur

  • hachage 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. le Sel de Hachage des mots de passe à l'aide de NodeJS crypto

1
répondu rahil471 2016-08-01 05:06:37

il s'agit d'une version modifiée de la réponse de @Matthews, en utilisant le Typographie

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));
            }
        });
    });
}
1
répondu André Pena 2017-08-12 17:02:25