什么是用于Node的bcrypt替代方案?

36

我已经尝试了数天在我的Windows机器上安装bcrypt,但没有成功。其中一个依赖项(Windows 7 SDK)无论我如何尝试网络上的各种建议都不愿意安装。

我需要一个没有任何依赖关系的良好的bcrypt替代方案。


2
Node.js的内置加密功能有什么问题吗? - josh3736
2
@josh3736 我对加密的经验不足以回答这个问题。但我注意到大多数项目和示例使用bcrypt,如果有其他解决方案(如下所述)没有任何依赖关系,这似乎很奇怪。 - Kory
6个回答

30

2
我可能会使用其中之一,但我不确定您所说的“样板文件”是什么意思。您是指它做出了一些假设吗?例如密钥应该有多少位,加密类型等等?除此之外,这两种方法在任何其他方面是否不同,比如更容易或更难规避? - Kory
没错,simplecrypt非常适合只进行简单的字符串编码和解码,而无需配置盐、哈希等。bcryptjs得到了其他模块(如https://npmjs.org/package/loopback)的企业支持,因此对于任何更复杂的事情,我肯定会选择它而不是simplecrypt。 - enducat
1
bcryptjs是否具有与bcrypt相同的安全功能? - Amiga500

24

截至2020年4月24日,在crypto模块中有一种很好的内置方法可以使用scrypt来进行密码哈希处理。

// Using the built in crypto module

const { scryptSync, randomBytes } = require("crypto");


// Any random string here (ideally should be atleast 16 bytes)

const salt = randomBytes(16).toString("hex")


// Pass the password string and get hashed password back
// ( and store only the hashed string along with the salt in your database)
// {hashedPassword}${salt}

const getHash = (password) => scryptSync(password, salt, 32).toString("hex");


如何解密相同的内容? - user12550010
1
你无法解密它。它是单向散列。若要比较密码,只需将用户输入的文本进行哈希处理,然后将其与存储的值进行比较即可。 - Malik Bagwala
我尝试比较密码以进行身份验证。我尝试了您所说的对用户输入密码进行哈希处理并比较哈希结果的方法。但是结果不同。 - Bryan Lumbantobing
1
如果您不存储盐值,那么以后如何验证密码呢? - Meekohi
1
@Meekohi 我通过更新评论来更正我的说法。 - Malik Bagwala

11

这里是 @malik-bagwala 的改进版本,增加了 JsDocs、类型和匹配密码函数。

import { randomBytes, scryptSync } from 'crypto';

// Pass the password string and get hashed password back
// ( and store only the hashed string in your database)
const encryptPassword = (password: string, salt: string) => {
  return scryptSync(password, salt, 32).toString('hex');
};

/**
 * Hash password with random salt
 * @return {string} password hash followed by salt
 *  XXXX till 64 XXXX till 32
 *
 */
export const hashPassword = (password: string): string => {
  // Any random string here (ideally should be at least 16 bytes)
  const salt = randomBytes(16).toString('hex');
  return encryptPassword(password, salt) + salt;
};

// fetch the user from your db and then use this function

/**
 * Match password against the stored hash
 */
export const matchPassword = (password: string, hash: string): Boolean => {
  // extract salt from the hashed string
  // our hex password length is 32*2 = 64
  const salt = hash.slice(64);
  const originalPassHash = hash.slice(0, 64);
  const currentPassHash = encryptPassword(password, salt);
  return originalPassHash === currentPassHash;
};


2
使用crypto.timingSafeEqual代替originalPassHash === currentPassHash来比较两个哈希值,以防止时序攻击。您可以在此SO答案中找到一个实现。 - Advena
1
喜欢这个!通过将盐附加到已盐化的哈希中来加密密码的好方法。天才!除非有人找出算法:D - Zilvinas
@Advena 我不认为 crypto.timingSafeEqual 是必要的,因为它确实防止了可能让攻击者猜测值的时间攻击:由于它们是经过哈希处理的,我不认为攻击者能够做太多事情。 - undefined
1
@Zitoun,确实,你是对的。我后来了解到value1 === value2是一个问题,而value1Hashed === value2Hashed是一种原始的时间安全比较。这里有一篇文章介绍了这个问题:双 HMAC 比较(注意:我知道密码哈希 != HMAC;然而,两者都使用哈希算法)。 - undefined

3
如果有人遇到类似的问题,可以尝试使用bcryptjs,它是一个用JavaScript编写的优化版bcrypt,零依赖且与C++ bcrypt兼容。

1
谢谢!帮了大忙。我遇到了问题,因为在 arm64-linux 上没有构建 bcrypt(我正在使用 M1 Mac 上的 Alpine Linux 构建 Docker 镜像)。 - Timotej Leginus
“优化” - 比bcrypt慢30%(https://github.com/dcodeIO/bcrypt.js#security-considerations)。 - Advena
@Advena,当涉及哈希函数时,“越慢越好”对吗?(参考链接:https://security.stackexchange.com/questions/150620/what-is-the-purpose-of-slowing-down-the-calculation-of-a-password-hash) - Mat J
@MatJ 这是个好问题!当你从数据库中验证密码哈希时,你可能想让它变慢一点。但是当你将它们哈希保存到数据库中时呢?那 30% 的减速 可能会很烦人。 - Nux
@MatJ 就像 Nux 所说的那样。Bcrypt 设计上就是慢的 - 没有必要使用高级编程语言(JavaScript)重写它,使它变得更慢。 - Advena

0

在编写加密代码时,你应该使用内置的加密模块。它基本上是 OpenSSL 的绑定,是一个快速、稳定、安全且经过充分验证的加密库。尝试实现自己的加密算法(或使用其他人未经验证的加密算法)会导致灾难。

如果你想要加密数据,只需调用 crypto.createCipher,它将返回一个可读/可写的 Stream。将数据写入流中,它将发出带有加密数据的数据事件。

例如:

var stream = crypto.createCipher('aes192', 'mysecretpassword');
stream.on('data', function(enc) {
    // enc is a `Buffer` with a chunk of encrypted data
});

stream.write('some secret data');
stream.end();

11
如果你正在存储密码,这并不推荐。你需要使用盐,否则你的数据库中所有密码都有被破解的风险,例如使用彩虹表。只是一个想法。干杯! - Dave Jensen

0

可以使用Argon2i、Argon2d或Argon2id(默认)进行哈希,并验证密码是否与哈希匹配。

https://www.npmjs.com/package/argon2

const password = 'password123';

async function hashPassword(password) {
  try {
    const hashedPassword = await argon2.hash(password);
    console.log('Hashed password:', hashedPassword);
  } catch (error) {
    console.error('Error hashing password:', error);
  }
}

hashPassword(password);

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