Node.js - 如何比较两个 bcrypt 哈希密码

10

你好,我需要一些关于这个问题的帮助。我已经搜索了解决方案,但还没有找到。

我想比较两个相同密码的 bcrypt 哈希密码,该怎么做?

例如:

我有这两个哈希密码,它们都来自相同密码的 bcrypt:

var password = E@Js#07Do=U$
var hash1 = $2a$10$fKAyjaG0pCkisZfRpKsBxursD6QigXQpm1TaPBDZ4KhIZRguYPKHe
var hash2 = $2a$10$mgApOcRIp7RSK3lRIIlQ5e/GjVFbxAFytGAEc0Bo17..r8v2pPR22
// that's not working for me
bcrypt.compare(passwordHash, userPasswordLoginHash, function(err, isMatch) {
   if (err) throw err;
   if(isMatch){
      console.log('correct password!')
   }
   callback(null, isMatch);
});

我该如何使用bcryptjs npm包来比较它们以确定它们是否来自同一个密码?

1
我认为你只能将明文密码与哈希密码进行比较。 - Tom Berghuis
4个回答

23

这是由于真正的密码哈希作为核心安全属性而设计的,因此不可能发生。

如果您可以比较两个密码哈希值(不知道原始密码),那么如果攻击者破解了系统中的一个密码,他们将立即知道使用该密码的所有用户的密码,而无需进行任何其他工作。很明显,这将是一个糟糕的事情。

例如,如果使用不适合于密码存储的哈希(如MD5)存储密码,则如果有50个用户使用“password”作为密码,则所有哈希密码的MD5哈希值都相同(“5f4dcc3b5aa765d61d8327deb882cf99”),并且破解其中一个将揭示所有50个用户的密码。

像bcrypt这样的现代密码哈希算法是不可能做到这一点的。唯一的“比较”现代密码哈希的方法是提前知道明文密码,然后对每个哈希中的盐应用算法。即使两个用户使用相同的密码,攻击者也必须分别执行相同的昂贵计算来破解它们,因为唯一的盐使每个哈希值都是唯一的。

更普遍而言,这可能听起来有些大胆,但任何系统或管理员永远不应该比较两个不同用户的密码,因为这没有合法的使用情况。一旦密码存储,用户密码应该是100%独立和100%不透明的对于系统来说。如果一个系统或业务案例需要这种比较,它应该重新设计以消除该要求。

实际上有使用案例。例如,我正在为匿名性哈希用户ID,但为了知道客户端上两个帖子是否由同一用户发布,我想比较它们的哈希ID... - Chris
如果用户更改了他们的密码,您想要验证他们没有重复使用过他们最近的24个密码中的任何一个,该怎么办?您需要存储先前的24个密码哈希,并将新密码与每个密码进行比较。理想情况下,您不希望为24个检查中的每一个重新计算新密码的哈希值。 - obsessiveprogrammer
2
这正是密码历史验证通常的做法。如果有什么比不对某人的当前密码进行哈希更糟糕的事情,那就是不对他们的密码历史进行哈希,允许攻击者研究所有先前的密码,并揭示用户选择密码的心理学。 - Royce Williams
那么,如果没有某种比较机制,也没有保存明文密码,存储的哈希值如何“用于”知道用户在未来是否输入了正确的密码呢? - Daniel
不同类型的比较。 :) 当用户提供明文时,您已经拥有了明文 - 因此您可以对明文进行哈希处理,然后将结果与存储的哈希进行比较,以查看它们是否匹配。比较两个未知明文的哈希值是完全不同的问题。我的答案应该澄清为什么另一种选择是反功能特性。 - Royce Williams
显示剩余2条评论

5

使用bcrypt库,您可以将明文密码与使用相同库进行哈希的密码进行比较。

问题在于微服务架构非常不安全。如果我有一个前端传递未经哈希处理的密码到后端,那么在系统后端中与哈希值进行比较之前,未经哈希处理的密码可能会被记录(可能在多个位置)。


3
没错,它不一定要是微服务架构。使用bcrypt的任何架构本质上都涉及通过网络传输明文密码?(HTTPS有助于解决问题,但并非完美解决方案)。 - luanped
1
这不是微服务架构的问题,而是实现方式的问题。它永远不应该记录那些字段,没有商量的余地。 - jwenting
1
在实践中,这实际上会导致不安全的明文密码存储/传输。 - R.Moeller
许多日志库都提供过滤器,因此您必须传递一个记录器实例以及当前密码以进行过滤。您甚至可以轻松地编写一个包装器来执行此操作。但是,当涉及到安全问题时,“它永远不应该”(如@jwenting所说)是一个不充分的前提条件。 - NotX

4

使用bcrypt库,您可以比较明文密码和使用相同库进行哈希处理的密码。

假设您已经对密码进行了哈希处理:

const myPlaintextPassword = 'E@Js#07Do=U$'
bcrypt.hash(myPlaintextPassword, saltRounds, function(err, hash) {
  // Store hash in your password DB.
  // example output, taking your hash
  // hash = $2a$10$fKAyjaG0pCkisZfRpKsBxursD6QigXQpm1TaPBDZ4KhIZRguYPKHe
});

您可以像这样进行比较:

// db query, get hashed password, found hash
// hash = $2a$10$fKAyjaG0pCkisZfRpKsBxursD6QigXQpm1TaPBDZ4KhIZRguYPKHe
// User input again:
const myPlaintextPassword = 'E@Js#07Do=U$'
bcrypt.compare(myPlaintextPassword, hash, function(err, res) {
  // res is true as the original password is the same
  // res == true
});

2
我认为问题不在于如何验证密码,而在于如何比较两个哈希值以查看它们是否是相同的密码。 - Royce Williams
如果您已经回答了那部分内容,那么请忽略。以下是如何进行比较的说明,以防OP在实现密码验证时感到困惑。 - 1565986223

0

为了更高的安全性,您可以在前端加密密码,然后在后端解密并进行比较。


前端代码是公开的,这意味着您可以反向工程请求,这并不提供额外的安全性,只是浪费时间。 - Tyler2P
在任何其他情况下,他都可以使用 SSL。 - 5skr0ll3r

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接