You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

NodeJS使用Bcrypt做哈希对比时错误对比字符串仍返回true问题求助

问题排查结果

核心诱因

Bcrypt算法本身存在固定输入长度限制:仅对明文输入的前72个字节做哈希计算,超出部分会被直接丢弃。
你序列化后的userData对象JSON字符串结构为:

{"dateNow":"xxx","userId":xxx,"passwordHash":"xxx","email":"xxx"}

常规场景下,dateNow、userId、email三个字段拼接后的JSON内容总长度已经覆盖了前72个字节,passwordHash的内容完全落在72字节之外,所以修改passwordHash的值不会影响Bcrypt的哈希计算结果,对比自然永远返回true;而修改前三个字段的内容会改变前72字节的内容,对比就能正常返回false。

存在的其他隐藏问题

你的hashUserData函数递归调用时未添加await关键字,当生成的哈希命中/或.的过滤规则时,返回的是Promise对象而非字符串,会导致后续逻辑异常。

解决方案

1. 调整字段顺序保证核心字段落在有效计算区间

将passwordHash放到对象的最前面,确保它的内容在Bcrypt的72字节处理范围内,生成哈希和对比校验时的对象字段顺序必须完全一致:

// 生成哈希时的对象结构
const userData = {
    passwordHash: foundUser.rows[0].user_password,
    dateNow,
    userId: foundUser.rows[0].user_id,
    email: foundUser.rows[0].user_email
};

// 对比校验时的对象结构也要保持相同字段顺序
const userDataToCheck = {
    passwordHash: foundUser.rows[0].user_password,
    dateNow: req.params.dateNow,
    userId,
    email: foundUser.rows[0].user_email
};

2. 修复哈希生成函数的递归问题

const hashUserData = async (userData, saltRounds) => {
    let hashedData = await bcrypt.hash(JSON.stringify(userData), saltRounds);
    if (hashedData.includes('/') || hashedData.includes('.')) {
        // 补充await关键字,等待递归结果返回
        hashedData = await hashUserData(userData, saltRounds);
    }
    console.log('hashUserData', hashedData)
    return hashedData;
};

3. (推荐优化)替换URL安全处理逻辑

递归过滤/和.的方式效率极低,极端场景下会触发栈溢出,建议直接对生成的哈希做字符替换适配URL规则,无需重复生成哈希:

// 生成哈希后转为URL安全格式
const toUrlSafeHash = (rawHash) => {
    return rawHash.replace(/\+/g, '-').replace(/\//g, '_').replace(/\./g, '~');
};

// 对比前将URL中的安全哈希转回原始格式
const fromUrlSafeHash = (urlSafeHash) => {
    return urlSafeHash.replace(/-/g, '+').replace(/_/g, '/').replace(/~/g, '.');
};

内容的提问来源于stack exchange,提问作者Duncan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.26 06:15:05