在将用户的refresh_token存储在数据库中之前,我使用bcryptjs对其进行散列。
当将散列字符串与JWT进行比较时,下面的计算结果似乎总是为true,我在https://bcrypt-generator.com/上也得到了相同的行为
例如,散列$2a$10$z4rwnyg.cVtP2SHt3lYj7.aGeAzonmmzbxqCzi2UW3SQj6famGaqW与以下两个JWT匹配
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiI2NTZlODdkNi1jMmVkLTRmN2ItOTU2Zi00NDFhMWU1NjA2MmQiLCJpYXQiOjE2Mzk1OTg2MDIsImV4cCI6MTY0MjE5MDYwMn0.aJlzFHhBMGO4J7vlOudqOrOFnL1P-yEGrREgdaCXlxU
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiI2NTZlODdkNi1jMmVkLTRmN2ItOTU2Zi00NDFhMWU1NjA2MmQiLCJpYXQiOjE2Mzk2MDY4ODgsImV4cCI6MTY0MjE5ODg4OH0.vo4HKLXuQbT0Yb0j21M4xl-rakxyE5wINjuGdkPuSJY
您也可以在网站上验证它们是否都会导致“匹配”。
truebcrypt.compareSync(jwt1,h) < true > bcrypt.compareSync(jwt2,h) < true这是我自己的JS代码,它还复制哈希匹配:
// Login Logic
const refresh_token: string = jwt.sign({ userId }, authSecrets.refresh_secret, { expiresIn: '30d' });
const hash_refresh = bcrypt.hashSync(refresh_token);
await UserModel.update({
id: user.id,
refresh_token: hash_refresh,
});// Refresh logic
// 'value' is the payload after using joi to validate it
const claims: any = jwt.verify(value.refresh_token, authSecrets.refresh_secret);
user = await UserModel.get(claims.userId);
if (!bcrypt.compareSync(value.refresh_token, user.refresh_token)) {
// This never happens with any JWT!
return response(401, 'Refresh Token is incorrect');
}为什么会发生这种情况?字符串显然是不同的(虽然不是很多)。
发布于 2021-12-15 23:03:37
哈希冲突是因为bcrypt 只散列输入的前72字节。 (在大多数实现中)。
这在bcryptjs和bcrypt npm包的自述文件中都有记录:
最大输入长度为72个字节(注意,UTF8编码的字符最多需要4个字节),生成的哈希长度为60个字符。
根据bcrypt实现,只使用字符串的前72个字节。当匹配密码时,任何额外的字节都会被忽略。请注意,这不是前72个字符。字符串可能包含少于72个字符,而占用超过72个字节(例如,包含emojis的UTF-8编码字符串)。
(这是一个客观上糟糕的设计,考虑到这是为了用户安全.如果输入超过72字节,bcryptjs库实际上应该总是抛出异常。
我注意到bcrypt是为人提供的(即非随机)密码而设计的,而不是作为一个通用的消息摘要算法。如果您不需要向随机生成的密码(比如您的refresh_token值)添加盐,那么您可能应该为此使用类似SHA-2家族算法(例如SHA-256,而不是SHA-1)的方法。
https://stackoverflow.com/questions/70371126
复制相似问题