121 votes

Comment node.bcrypt.js compare-t-il les mots de passe hachés et en clair sans le sel ?

De github :

Pour hacher un mot de passe :

var bcrypt = require('bcrypt');
bcrypt.genSalt(10, function(err, salt) {
    bcrypt.hash("B4c0/\/", salt, function(err, hash) {
        // Store hash in your password DB.
    });
});

Pour vérifier un mot de passe :

// Load hash from your password DB.
bcrypt.compare("B4c0/\/", hash, function(err, res) {
    // res == true
});
bcrypt.compare("not_bacon", hash, function(err, res) {
    // res = false
});

D'après ce qui précède, comment se fait-il qu'aucune valeur de sel ne soit impliquée dans les comparaisons ? Qu'est-ce qui m'échappe ici ?

121voto

Bill Points 9537

Le sel est incorporé dans le hachage (en clair). La fonction de comparaison extrait simplement le sel du hachage et l'utilise ensuite pour hacher le mot de passe et effectuer la comparaison.

39voto

prashanth Points 181

Bcrypt compare les mots de passe hachés et en clair sans la chaîne de sel car le mot de passe haché contient la chaîne de sel que nous avons créée au moment du hachage.

Par exemple :

Prenez ce mot de passe simple :

546456546456546456456546111

Mot de passe haché du texte brut ci-dessus en utilisant Bcrypt :

2b$10$uuIKmW3Pvme9tH8qOn/H7uZqlv9ENS7zlIbkMvCSDIv7aup3WNH9W

Donc dans le mot de passe haché ci-dessus, il y a trois champs délimités par $ symbole.

i) Première partie $2b$ identifie la version de l'algorithme bcrypt utilisée.

ii) Deuxième partie $10$ 10 est le facteur coût (rien que des rondes de sel pendant que nous créons la chaîne de sel. Si nous faisons 15 tours, alors la valeur sera de $15$

iii) La troisième partie est la première 22 caractères (qui n'est rien d'autre qu'une chaîne de sel) Dans ce cas, c'est

uuIKmW3Pvme9tH8qOn/H7u

La chaîne restante est le mot de passe haché. Donc, fondamentalement, le saltedHash = chaîne de sel + mot de passe haché pour se protéger des attaques de type rainbow table.

30voto

Alappin Points 646

J'ai eu la même question que l'auteur de la première affiche et il m'a fallu chercher un peu et essayer différentes choses pour comprendre le mécanisme. Comme d'autres l'ont déjà souligné, le sel est concaténé au hachage final. Cela signifie donc deux choses :

  1. L'algorithme doit connaître la longueur du sel.
  2. Il faut également connaître la position du sel dans la chaîne finale, par exemple s'il est décalé d'un nombre spécifique par rapport à la gauche ou à la droite.

Ces deux éléments sont généralement codés en dur dans l'implémentation, par exemple, la source de l'implémentation de bcrypt pour bcryptjs définit la longueur du sel comme étant de 16

/**
* @type {number}
* @const
* @private
*/

var BCRYPT_SALT_LEN = 16;

Pour illustrer le concept de base de l'idée, si l'on voulait le faire manuellement, cela ressemblerait à ce qui suit. Je ne recommande pas d'implémenter ce genre de choses vous-même quand il existe des bibliothèques pour le faire.

var salt_length = 16;
var salt_offset = 0;

var genSalt = function(callback)
{
    var alphaNum = '0123456789abcdefghijklmnopqurstuvwxyzABCDEFGHIJKLMNOPQURSTUVWXYZ';
    var salt = '';
    for (var i = 0; i < salt_length; i++) {
        var j = Math.floor(Math.random() * alphaNum.length);
        salt += alphaNum[j];
    }
    callback(salt);
}

// cryptographic hash function of your choice e.g. shar2
// preferably included from an External Library (dont reinvent the wheel)
var shar2 = function(str) {
    // shar2 logic here 
    // return hashed string;
}

var hash = function(passwordText, callback)
{
    var passwordHash = null;
    genSalt(function(salt){
        passwordHash = salt + shar2(passwordText + salt);
    });

    callback(null, passwordHash);
}

var compare = function(passwordText, passwordHash, callback)
{
    var salt = passwordHash.substr(salt_offset, salt_length);
    validatedHash = salt + shar2(passwordText + salt);

    callback(passwordHash === validatedHash);   
}

// sample usage
var encryptPassword = function(user)
{
    // user is an object with fields like username, pass, email
    hash(user.pass, function(err, passwordHash){
        // use the hashed password here
        user.pass = passwordHash;
    });

    return user;
}

var checkPassword = function(passwordText, user)
{
    // user has been returned from database with a hashed password
    compare(passwordText, user.pass, function(result){
        // result will be true if the two are equal
        if (result){
            // succeeded
            console.log('Correct Password');
        }
        else {
            // failed
            console.log('Incorrect Password');
        }
    });
}

5voto

babaliaris Points 27

Comme je me suis moi-même posé la même question, je sais exactement à quoi vous pensez.

Vous avez une idée fausse entre "Clé secrète" qui est utilisé dans Cryptographique et le "sel" qui est utilisé pour ralentir le processus de cryptage et rendre plus difficile l'utilisation de la force brute par les pirates.

Lorsque vous utilisez le mot de passe simple et le sel pour générer le hachage, ce hachage utilise comme clé secrète le mot de passe lui-même ! Ainsi, la prochaine fois que vous essayerez de le comparer avec un mot de passe ordinaire, ce dernier devra être exactement le même que celui que vous avez utilisé pour générer le hachage ! C'est pourquoi vous n'avez pas besoin de le stocker ailleurs car il est toujours fourni par l'utilisateur lors des étapes d'enregistrement et de connexion !

4voto

wolfrevo Points 161

Il s'agit simplement d'une chaîne de longueur fixe.

console.log("");
var salt = bcrypt.genSaltSync(10);
console.log(salt);
hash = bcrypt.hashSync("foobar", salt);
console.log(hash);

console.log("");
var salt = bcrypt.genSaltSync(10);
console.log(salt);
hash = bcrypt.hashSync("foobar", salt);
console.log(hash);

console.log("");
var salt = bcrypt.genSaltSync(10);
console.log(salt);
hash = bcrypt.hashSync("foobar", salt);
console.log(hash);

$2a$10$onmcKV.USxnoQAsQwBFB3e
$2a$10$onmcKV.USxnoQAsQwBFB3eytL3UZvZ5v/SudaWyaB9Vuq9buUqGO2

$2a$10$mwQfdyVS9dsO4SuxoR5Ime
$2a$10$mwQfdyVS9dsO4SuxoR5ImeG7atz7RXGRXb.c0VHp5zSn1N2VOA.Vq

$2a$10$uVUuJr6LryjchhKEg6PH7u
$2a$10$uVUuJr6LryjchhKEg6PH7unTw8aJGK0i3266c5kqDBLJkf80RHEpq

$2a$10$Y.upG5/54zvJyZacRxP17O
$2a$10$Y.upG5/54zvJyZacRxP17OH60BC0hQRMNfQjJxSWE77fyBrbzalmS

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X