hash - 多个 JWT 的匹配哈希 JWT

标签 hash jwt bcrypt bcryptjs

我使用 bcryptjs 对用户的刷新 token 进行哈希处理,然后再将其存储到数据库中。

当将散列字符串与 JWT 进行比较时,以下内容似乎总是评估为 true,我在 https://bcrypt-generator.com/ 上也得到了相同的行为

例如,哈希值 $2a$10$z4rwnyg.cVtP2SHt3lYj7.aGeAzonmmzbxqCzi2UW3SQj6famGaqW 与以下两个 JWT 匹配

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiI2NTZlODdkNi1jMmVkLTRmN2ItOTU2Zi00NDFhMWU1NjA2MmQiLCJpYXQiOjE2Mzk1OTg2MDISimV4cCI6MTY0MjE 5MDYwMn0.aJlzFHhBMGO4J7vlOudqOrOFnL1P-yEGrREgdaCXlxU

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiI2NTZlODdkNi1jMmVkLTRmN2ItOTU2Zi00NDFhMWU1NjA2MmQiLCJpYXQiOjE2Mzk2MDY4ODgsImV4cCI6MTY0MjE 5ODg4OH0.vo4HKLXuQbT0Yb0j21M4xl-rakxyE5wINjuGdkPuSJY

您也可以在网站上验证它们是否都产生“匹配”

  1. 转到https://bcrypt-generator.com/并打开浏览器控制台。

  2. 在控制台中输入这些行:

    > var jwt1 = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiI2NTZlODdkNi1jMmVkLTRmN2ItOTU2Zi00NDFhMWU1NjA2MmQiLCJpYXQiOjE2Mzk1OTg2MDIsImV4cCI6MTY0MjE5MDYwMn0.aJlzFHhBMGO4J7vlOudqOrOFnL1P-yEGrREgdaCXlxU"
    < undefined
    
    > var jwt2 = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiI2NTZlODdkNi1jMmVkLTRmN2ItOTU2Zi00NDFhMWU1NjA2MmQiLCJpYXQiOjE2Mzk2MDY4ODgsImV4cCI6MTY0MjE5ODg4OH0.vo4HKLXuQbT0Yb0j21M4xl-rakxyE5wINjuGdkPuSJY"
    < undefined
    
    > var h = "$2a$10$z4rwnyg.cVtP2SHt3lYj7.aGeAzonmmzbxqCzi2UW3SQj6famGaqW"
    < undefined
    
  3. 然后在控制台中输入这些行,观察它们如何返回true:

    > bcrypt.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');
}

为什么会发生这种情况?字符串明显不同(尽管相差不是很多)。

最佳答案

哈希冲突是因为 bcrypt only hashes the first 72 bytes of input (在大多数实现中)。

这在 bcryptjsbcrypt npm 包的自述文件中都有记录:

bcryptjs :

The maximum input length is 72 bytes (note that UTF8 encoded characters use up to 4 bytes) and the length of generated hashes is 60 characters.

bcrypt :

Per bcrypt implementation, only the first 72 bytes of a string are used. Any extra bytes are ignored when matching passwords. Note that this is not the first 72 characters. It is possible for a string to contain less than 72 characters, while taking up more than 72 bytes (e.g. a UTF-8 encoded string containing emojis).

(考虑到这是为了用户安全,这是一个客观上糟糕的设计......如果输入超过 72 字节(IMO),bcryptjs 库确实应该始终抛出异常)

我注意到 bcrypt 是为人类提供的(即非随机)密码而设计的,而不是作为通用的消息摘要算法。鉴于您不需要向随机生成的密码添加盐(例如您的 refresh_token 值),您可能应该使用 SHA-2 系列算法(例如 SHA-256,但不是 SHA-) 1)为此。

关于hash - 多个 JWT 的匹配哈希 JWT,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/70371126/

相关文章:

python - __hash__ 的返回值是如何使用的?

java - 看似简单的 FNV1 哈希实现导致大量冲突

java - 在 Payara 5.183 上使用 MicroProfile JWT Auth 1.1 注入(inject)的 JsonWebToken 为空

perl - 在 Perl 中使用 Bcrypt

python - 使用 php 格式的函数 password_hash() 的 Django 密码哈希器

csv - 读取CSV解析数据并存储在Hash中

c# - 使用 MD5.Create 和 MD5CryptoServiceProvider 有什么区别?

php - Symfony 3 + JWT + angular 2 + 身份验证(选项方法)

authentication - K6 - 身份验证 - 获取授权 token

javascript - bcrypt 密码与比较方法进行比较总是会出现错误