node.bcrypt.js如何在没有盐的情况下比较散列密码和明文密码?

SCh*_*ang 79 bcrypt node.js

来自github:

哈希密码:

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

要检查密码:

// 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
});
Run Code Online (Sandbox Code Playgroud)

从上面来看,如何在比较中不涉及盐值?我在这里错过了什么?

Bil*_*ill 82

盐被合并到散列中(作为明文).compare函数只是将salt拉出哈希值,然后使用它来哈希密码并执行比较.

  • 如果攻击者知道任何特定哈希的盐并不重要,那不是秘密.为每个密码使用不同的盐意味着攻击者无法使用常用值预先计算哈希值.如果每个密码都使用不同的盐,则需要为每个密码重新计算任何表,这使得它们无用. (10认同)
  • bcrypt是一个标准,并且始终以相同的格式将salt与哈希连接起来.您在加密时提供salt,并将其合并到哈希中.bcrypt只能解密最初使用bcrypt加密的数据,否则,你是对的 - 它无法知道哪个部分是哈希,哪个部分是盐. (6认同)
  • 谢谢,你能指出任何链接来阅读这个吗? (4认同)
  • 我还是不明白。在比较过程中,如果不提供盐,它如何知道散列的哪一部分是盐? (3认同)
  • 好吧,我们得到它:salt与哈希一起存储.bcrypt是开源的,所以这意味着每个人都知道它是如何存储的.所以你知道如何提取它,或者如何从纯文本密码生成哈希.这有助于保护密码免受扫描彩虹表的哈希,这基本上是盐背后的主要思想? (3认同)
  • 看看这种方式,如果攻击者像数据库那样知道某些用户的盐是否重要:`column_password = hash,column_salt = salt` vs`column_password = hash_salt`.攻击者仍然拥有相同的信息.盐点是让每个密码都变得随机和大,以至于不太可能有人预先计算了它. (2认同)

Ala*_*pin 24

我也和原版海报一样有同样的问题,看了一下,尝试了不同的东西来理解机制.正如其他人已经指出的那样,盐被连接到最终的哈希值.所以这意味着几件事:

  1. 算法必须知道盐的长度
  2. 还必须知道最终字符串中盐的位置.例如,如果从左或右偏移特定数字.

这两件事通常在实现中被硬编码,例如bcryptjs的bcrypt实现源将salt长度定义为16

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

var BCRYPT_SALT_LEN = 16;
Run Code Online (Sandbox Code Playgroud)

因此,为了说明想法背后的基本概念,如果想要手动完成,它将类似于下面的内容.当你有可以实现的库时,我不建议你自己实现这样的东西.

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');
        }
    });
}
Run Code Online (Sandbox Code Playgroud)


小智 8

Bcrypt 比较不​​带盐串的散列密码和明文密码,因为散列密码包含我们在散列时创建的盐串。

例如 :

使用这个普通密码:

546456546456546456456546111

使用 Bcrypt 对上述纯文本的哈希密码:

$2b$10$uuIKmW3Pvme9tH8qOn/H7uZqlv9ENS7zlIbkMvCSDIv7aup3WNH9W

所以在上面的哈希密码中,有3个字段以$符号分隔。

i) 第一部分$2b$标识使用的 bcrypt 算法版本。

ii) 第二部分$10$ 10 是成本因素(我们创建盐串时只有盐轮。如果我们做 15 轮,那么价值将是$15$

iii) 第三部分是前22 个字符(也就是盐串)在这种情况下它是

uuIKmW3Pvme9tH8qOn/H7u

剩下的字符串是散列密码。所以基本上,saltedHash = salt string + hashedPassword 来防止彩虹表攻击。

  • 由于其简洁的解释,这应该是公认的答案 (5认同)

bab*_*ris 5

因为我自己也有同样的问题,所以我很清楚你在想什么。

您对加密算法中使用的“密钥”和用于减慢加密过程并使黑客更难使用暴力的“盐”有误解。

当您使用纯密码和盐生成哈希时,该哈希使用密码本身作为秘密密钥!因此,下次您尝试将其与纯密码进行比较时,该纯密码必须与您用于生成哈希的密码完全相同!因此,这就是为什么您不必将其存储在其他地方,因为它始终由用户在注册和登录步骤中提供!