Como node.bcrypt.js compara senhas em hash e em texto simples sem o sal?

95

Do github :

Para hash uma senha:

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

Para verificar uma senha:

// 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
});

De cima, como pode não haver valores de sal envolvidos nas comparações? O que estou perdendo aqui?

SChang
fonte

Respostas:

95

O sal é incorporado ao hash (como texto simples). A função de comparação simplesmente extrai o sal do hash e o usa para fazer o hash da senha e realizar a comparação.

Conta
fonte
1
Eu ainda não entendo. Durante a comparação, como ele sabe qual parte do hash é o sal, se você não fornecer o sal?
Segunda-
6
bcrypt é um padrão e sempre concatena o salt com o hash no mesmo formato. Você fornece o sal ao criptografar e ele é incorporado ao hash. bcrypt só será capaz de descriptografar dados que foram criptografados originalmente usando bcrypt, caso contrário, você está certo - não haveria como ele saber qual parte é o hash e qual parte é o salt.
Bill de
4
Ok, entendemos: o sal é armazenado com o hash. O bcrypt é open source, então isso significa que todos sabem exatamente como ele o armazena. Então você sabe como extraí-lo ou como gerar hash a partir de uma senha em texto simples. Como isso ajuda a proteger as senhas contra a varredura de tabelas de rainbow em busca de hashes, que é basicamente a ideia principal por trás do salt?
Vitaliy Lebedev
13
Não importa se o invasor conhece o sal de um hash específico, não é segredo. Usar um sal diferente para cada senha significa que o invasor não pode pré-calcular hashes usando valores comuns. Com um sal diferente em cada um, eles precisariam recalcular todas as tabelas para cada senha, o que as tornaria inúteis.
Bill
3
olhada Desta forma, não importa se atacante sabe sal para determinado usuário na base de dados como este: column_password = hash, column_salt = saltvs column_password = hash_salt. o invasor ainda tem as mesmas informações. O ponto importante é tornar cada senha tão aleatória e maior que se torne improvável que alguém a tenha pré-computado.
Muhammad Umer
27

Eu também tive a mesma pergunta que o pôster original e dei uma olhada em volta e tentei coisas diferentes para entender o mecanismo. Como já foi apontado por outros, o sal é concatenado ao hash final. Portanto, isso significa algumas coisas:

  1. O algoritmo deve saber o comprimento do sal
  2. Também deve saber a posição do sal na corda final. por exemplo, se deslocado por um número específico da esquerda ou direita.

Essas duas coisas são geralmente codificadas na implementação, por exemplo, a fonte de implementação bcrypt para bcryptjs define o comprimento do sal como 16

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

var BCRYPT_SALT_LEN = 16;

Portanto, para ilustrar o conceito básico por trás da ideia, se alguém quisesse fazê-lo manualmente, seria semelhante ao seguinte. Eu não recomendo implementar coisas como essa você mesmo, quando houver bibliotecas que você possa obter para fazê-lo.

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');
        }
    });
}
Alappin
fonte
0

Porque eu mesmo fiz a mesma pergunta, sei exatamente o que você está pensando.

Você tem um equívoco entre "Chave Secreta", que é usada na Criptografia algoritmos , e "Sal", que é usada para desacelerar o processo de criptografia e tornar mais difícil para os hackers usarem a força bruta.

Quando você usa a senha simples e o salt para gerar o hash, esse hash usa como chave secreta a própria senha ! Portanto, da próxima vez que você tentar compará-la com uma senha simples, esta senha simples deve ser exatamente a mesma que você usou para gerar o hash! Portanto, é por isso que você não precisa armazená-lo em outro lugar, pois ele é sempre fornecido pelo usuário nas etapas de registro e login!

Babaliaris
fonte