nodejs 中的 bcrypt (2) : bcrypt 浅析

上一篇文章 nodejs 中的 bcrypt (1) : bcrypt 的特点与应用 通过一个 web 用户注册登录的案例,简述了 bcrypt 的特点、nodejs 中的 bcrypt 包,并附上了具体代码。

代码跑通了,但一些疑惑没有解决,比如:

  • 加盐哈希后得到的一长串字符串包含什么信息?
  • 对同一个密码,每次加盐哈希的得到的字符串都不一致,那么比对密码的过程是如何进行的?
  • 对比密码使用的 bcrypt.compare() 方法,其参数里并没有 salt 。是这个过程里盐值没用,还是它深藏别处?

这篇来进行解答。

第一个问题

加盐哈希后得到的一长串字符串包含什么信息?

运行以下这段代码三次

testHash.js

const bcrypt = require('bcrypt-nodejs')
const SALT_FACTOR = 10

bcrypt.genSalt(12, function (err, salt) {
  if (err) return next(err)
  bcrypt.hash('mypassword', salt, null, function (err, hash) {
    if (err) return next(err)
    console.log(hash);
  })
})

分别得到三个60位的字符串

$2a$10$dsZFPb.m2b6cXuw.fQAjneRk7u33cF9ZrWqywX4j5K5ymlhLFwRMS
$2a$10$mWm.uX4tSUKqa4Rk1/DQRuqm3zd.S5EaaWiIEQVrq8YxiebiqkMwy
$2a$10$oMpGJ3NhLD7OtX8lUCq6bOYr04SAgq00J2yLf/TsHZMulnmO9yJGi

容易发现,前7位都是相同的 $2a$10$

将参数 SALT_FACTOR 的值改为 12 ,再运行三次

$2a$12$5XhpRItT.wGNyVJk67QMPOchZQsHwBprwNBLEOot8cJOryihRa74W
$2a$12$0DK/6Q48hUkqq0im/9QyHee4Y/vrbchzmuWNHvapRr6A6aGGrdBxS
$2a$12$JUVxAjk5/ZStnxHgvK2IDetlWfD1jCpT69/cgkgA0vV49SmwS3RfG

这三个字符串的前七位也是相同的: $2a$12$

与之前的三个字符串相比,第 5 第 6 位的值从 10,变成了 12。与各自的参数 SALT_FACTOR 一致。

这个参数就是 bcrypt cost parameter ,它决定了迭代的次数,即哈希计算的“缓慢程度”(见上一篇)。

前 4 位的 $2a$ 则指示了算法的版本。老版本有 $2$ ,更新的版本有 $2x$ $2y$ $2b$

字符串的后半段,前 22 位是 salt 盐值,后 31 位就是对应密码的哈希部分。

总结一下

$2a$10$i5btSOiulHhaPHPbgNUGdObga/GC.AVG/y5HHY1ra7L0C9dpCaw8u
  • 2a 指示算法版本
  • 10 是 cost 参数
  • i5btSOiulHhaPHPbgNUGdO 是 salt
  • 最后的31位 bga/GC.AVG/y5HHY1ra7L0C9dpCaw8u 对应密码

这也回答了第一个问题。

后两个问题

比对密码的过程是如何进行的?

查看 bcrypt-nodejs 的源码,其中的 compareSync 函数:

function compareSync(data, encrypted) {
    /*
        data - [REQUIRED] - data to compare.
        encrypted - [REQUIRED] - data to be compared to.
    */

    if(typeof data != "string" ||  typeof encrypted != "string") {
        throw "Incorrect arguments";
    }

    var encrypted_length = encrypted.length;

    if(encrypted_length != 60) {
        return false;
    }

    var same = true;
    var hash_data = hashSync(data, encrypted.substr(0, encrypted_length-31));
    var hash_data_length = hash_data.length;

    same = hash_data_length == encrypted_length;

    var max_length = (hash_data_length < encrypted_length) ? hash_data_length : encrypted_length;

    // to prevent timing attacks, should check entire string
    // don't exit after found to be false
    for (var i = 0; i < max_length; ++i) {
        if (hash_data_length >= i && encrypted_length >= i && hash_data[i] != encrypted[i]) {
            same = false;
        }
    }

    return same;
}

函数的两个参数中,data 相当于密码明文,encrypted 相当于从数据库里读出的哈希字符串。

结合第一个问题中提到的知识,哈希值中 substr(0, encrypted_length-31) 这一段是salt。

所以,匹配的思路是:从哈希值字符串中提取出salt,将其添加到密码明文中,再进行一次加盐哈希运算。比对 得到的结果 和 旧有的哈希字符串,由此判断密码是否匹配。

按照这个思路,写一个简化的例子:

解决第一个问题的时候,我们对 'mypassword' 进行了6次加密。任取一个字符串,赋值给 hash

testCompare.js

const bcrypt = require('bcrypt-nodejs')

const hash = '$2a$12$5XhpRItT.wGNyVJk67QMPOchZQsHwBprwNBLEOot8cJOryihRa74W'
const saltInHash = hash.substr(0, hash.length-31)
const newHash = bcrypt.hashSync('mypassword', saltInHash)

console.log('TEST: ' + (newHash === hash))

运行结果:

TEST: true

密码匹配成功!

总结一下

  • bcrypt 直接将 salt 保存在里加密计算得到的哈希字符串里。
  • 匹配思路是
    • 从哈希值字符串中提取出salt,将其添加到密码明文中,再进行一次加盐哈希运算。
    • 比对 得到的结果 和 旧有的哈希字符串,由此判断密码是否匹配。

后两个问题也得到了解答。

参考资料

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
引用提到了一个在安装bcrypt时可能会遇到的问题。在Windows系统上安装bcrypt时,可能会收到一堆npm ERR错误。根据引用,解决这个问题的方法是安装Windows的构建工具,可以使用命令`npm install --g --production windows-build-tools`来安装。这将安装一些必要的工具和依赖,以便成功编译bcrypt。 另外,引用给出了一个使用bcrypt生成随机哈希密码的示例。可以看到,每次生成的密码都是随机的,这增加了安全性。如果您在安装bcrypt时遇到了问题,可以尝试使用上述方法来解决。 希望这能帮助您解决nodejs安装bcrypt失败的问题。如果还有其他疑问,请随时提问。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [解决windows10,nodejs 无法安装 Bcrypt](https://blog.csdn.net/weixin_40677825/article/details/119105025)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT3_1"}}] [.reference_item style="max-width: 33.333333333333336%"] - *2* [Nodejs使用npm安装 bcrypt 提示npm ERR错误](https://blog.csdn.net/m0_73194953/article/details/127817789)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT3_1"}}] [.reference_item style="max-width: 33.333333333333336%"] - *3* [[ Nodejs ] MD5 | Bcrypt 数据加密](https://blog.csdn.net/haodian666/article/details/126246096)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT3_1"}}] [.reference_item style="max-width: 33.333333333333336%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值