跨语言验证Yii2生成的bcrypt密码(Node.js实现)
Node.js验证Yii2生成的bcrypt哈希密码失败问题
我数据库中存储的用户密码是通过Yii2的Yii::$app->getSecurity()->generatePasswordHash($password);生成的哈希值,尝试用Node.js的bcrypt库验证时,以下代码始终返回false:
bcrypt.compare(plainTextPassword, hashedPasswordFromYii2, (err, res) => { if (err) { console.error(err); return; } console.log(res); // 密码匹配时应输出true });
Yii2生成哈希的源码实现如下:
protected function generateSalt($cost = 13) { $cost = (int) $cost; if ($cost < 4 || $cost > 31) { throw new InvalidArgumentException('Cost must be between 4 and 31.'); } // 获取20字节随机字符串 $rand = $this->generateRandomKey(20); // 生成指定Blowfish(bcrypt)算法和cost参数的前缀 $salt = sprintf('$2y$%02d$', $cost); // 将随机盐值转为要求的base64格式并追加,替换+为. $salt .= str_replace('+', '.', substr(base64_encode($rand), 0, 22)); return $salt; } public function generatePasswordHash($password, $cost = null) { if ($cost === null) { $cost = $this->passwordHashCost; } if (function_exists('password_hash')) { /* @noinspection PhpUndefinedConstantInspection */ return password_hash($password, PASSWORD_DEFAULT, ['cost' => $cost]); } $salt = $this->generateSalt($cost); $hash = crypt($password, $salt); // strlen()安全,因为crypt()只返回ascii字符 if (!is_string($hash) || strlen($hash) !== 60) { throw new Exception('生成哈希时发生未知错误。'); } return $hash; }
补充说明:数据库中的密码不能修改,也不能用Node.js重新生成哈希,必须兼容现有应用,恳请提供解决方案。
问题原因
Yii2生成的bcrypt哈希前缀为$2y$,这是PHP特有的标识前缀;而Node.js的bcrypt库默认只识别$2a$或$2b$前缀,因此直接验证会失败。
解决方案
验证前将哈希中的$2y$替换为$2a$(或$2b$,bcrypt对两者的处理逻辑完全一致)即可:
// 处理Yii2哈希的前缀 const processedHash = hashedPasswordFromYii2.replace(/^\$2y\$/, '$2a$'); bcrypt.compare(plainTextPassword, processedHash, (err, res) => { if (err) { console.error(err); return; } console.log(res); // 现在密码匹配时会正确返回true });
原理说明
$2y$是PHP为修复特定字符编码问题引入的前缀,其底层哈希算法和$2a$完全相同,仅仅是标识差异。替换前缀后,Node.js的bcrypt库就能正确解析哈希并完成验证,不会影响验证结果的准确性。
内容的提问来源于stack exchange,提问作者Irvan Hilmi
相关产品推荐
相关产品推荐

