在PHP和NodeJS之间比较BCrypt哈希值

45

我正在开发的一个应用程序中,node.js需要验证PHP创建的哈希值,反之亦然。

问题是,在PHP中生成的哈希值(通过Laravel的Hash类,它只使用了PHP的password_hash函数)在node.js中测试时返回false。

以下是node.js脚本:

var bcrypt = require('bcrypt');

var password = 'password';

var phpGeneratedHash  = '$2y$10$jOTwkwLVn6OeA/843CyIHu67ib4RixMa/N/pTJVhOjTddvrG8ge5.';
var nodeGeneratedHash = '$2a$10$ZiBH5JtTDtXqDajO6f4EbeBIXGwtcGg2MGwr90xTH9ki34SV6rZhO';

console.log(
  bcrypt.compareSync(password, phpGeneratedHash)  ? 'PHP passed' : 'PHP failed',
  bcrypt.compareSync(password, nodeGeneratedHash) ? 'nodejs passed' : 'nodejs failed'
);

输出结果为“PHP失败,nodejs通过”,但以下PHP脚本:

<?php

$password = 'password';

$phpGeneratedHash  = '$2y$10$jOTwkwLVn6OeA/843CyIHu67ib4RixMa/N/pTJVhOjTddvrG8ge5.';
$nodeGeneratedHash = '$2a$10$ZiBH5JtTDtXqDajO6f4EbeBIXGwtcGg2MGwr90xTH9ki34SV6rZhO';

print password_verify($password, $phpGeneratedHash)  ? 'PHP passed' : 'PHP failed';
print password_verify($password, $nodeGeneratedHash) ? 'nodejs passed' : 'nodejs failed';

输出 'PHP通过,nodejs通过'。

我在Ubuntu 14.04.1上使用PHP 5.5.18,node.js v0.10.32和npm bcrypt模块运行了测试。

3个回答

90

这个失败是因为php和node生成的bcrypt哈希类型不同。Laravel生成$2y$,而node生成$2a$。但好消息是2a2y之间的唯一区别是它们的前缀。

所以你可以使其中一个前缀与另一个类似。比如:

$phpGeneratedHash  = '$2y$10$jOTwkwLVn6OeA/843CyIHu67ib4RixMa/N/pTJVhOjTddvrG8ge5.';
$nodeGeneratedHash = '$2a$10$ZiBH5JtTDtXqDajO6f4EbeBIXGwtcGg2MGwr90xTH9ki34SV6rZhO';

转化为类似以下内容:

$phpGeneratedHash  = '$2y$10$jOTwkwLVn6OeA/843CyIHu67ib4RixMa/N/pTJVhOjTddvrG8ge5.';
$nodeGeneratedHash = '$2y$10$ZiBH5JtTDtXqDajO6f4EbeBIXGwtcGg2MGwr90xTH9ki34SV6rZhO';

请注意,我已将节点哈希的 $2a$ 替换为 $2y$。您可以通过以下方式轻松完成此操作:

PHP

$finalNodeGeneratedHash = str_replace("$2a$", "$2y$", $nodeGeneratedHash);

节点

finalNodeGeneratedHash = nodeGeneratedHash.replace('$2a$', '$2y$');

然后将phpGeneratedHashfinalNodeGeneratedHash进行比较。

注意:建议如果您在PHP中进行比较,请将NodeJS生成的哈希前缀更改为$2y$,如果您在NodeJS中进行比较,请将PHP生成的哈希前缀更改为$2a$


2
谢谢你的回答,现在一切都按预期工作了!我没有想到要查找bcrypt哈希结构。是否有明显的原因,为什么bcrypt模块不像PHP那样允许两种实现方式都可以工作? - wolfemm
2
@wolfemm,简单来说,他们正在使用不同类型/版本的bcrypt。 - majidarif
1
谢谢,谢谢,谢谢,谢谢。我一直在拼命尝试让Auth0数据库连接Laravel密码哈希bcrypt.compareSync()匹配。你这位友好的Stack Overflow陌生人救了我的一命。谢谢。 - James Furey
3
两年后。@JamesFurey,不用谢。我在做同样的事情时弄清楚了这一点,花了一段时间。 - majidarif
谢谢,这让我找对了方向。然后我使用了 .replace(/^\$2y/, "$2a")... 来自 https://dev59.com/GVoV5IYBdhLWcg3wmPzp#36043615/ - Ryan

7

我尝试计算之前所说的内容以获取可用的代码。正如您所见,我不需要替换任何内容。

在PHP 7.2.4方面:

<?php
$password = "test123";
    $hash = password_hash($password, PASSWORD_BCRYPT);
    echo $hash; // I get $2y$10$5EaF4lMSCFWe7YqqxyBnR.QmDu1XhoiaQxrOFw.AJZkGCYmpsWDU6

在NodeJS端:
安装bcryptjs包:npm i bcryptjs
var bcrypt = require('bcryptjs');
let hash1="$2y$10$5EaF4lMSCFWe7YqqxyBnR.QmDu1XhoiaQxrOFw.AJZkGCYmpsWDU6";
console.log(bcrypt.compareSync("test123", hash1)); // display true

可能是因为你正在使用 bcryptjs,而问题使用的是 bcrypt 库。 - Adarsh Madrecha
同时,bcryptjs 的性能较差,请看测试结果:https://medium.com/javascript-in-plain-english/node-js-bcrypt-vs-bcryptjs-benchmark-69a9e8254cc2 - Maxim Firsoff
@MaximFirsoff 这并不重要。这是在登录/密码更新时的一次性调用,算法通过重复有意地减慢速度以抵御攻击。一个慢的库会对此有所帮助吗?实际上并不会,因为攻击通常会针对原始用户数据...但它也不会真正伤害到什么。 (请注意:我并不是说bryptjs库是有意缓慢的,它只是比bcrypt库慢,因为它在JS中运行而不是本地运行) - DennisK
这就是我在寻找的解决方案,谢谢 @Nicolas Guérinet - not_null

0

在不同的编程语言中实现bcrypt可能会有所不同。

例如,在Node.js版本 bcrypt.js中,应用的盐长度为29个字符

    bcrypt.getSalt = function(hash) {
        if (typeof hash !== 'string')
            throw Error("Illegal arguments: "+(typeof hash));
        if (hash.length !== 60)
            throw Error("Illegal hash length: "+hash.length+" != 60");
        return hash.substring(0, 29);
    };

但是,在Go版本golang.org/x/crypto/bcrypt中,盐的大小为22个字节:

const (
    majorVersion       = '2'
    minorVersion       = 'a'
    maxSaltSize        = 16
    maxCryptedHashSize = 23
    encodedSaltSize    = 22
    encodedHashSize    = 31
    minHashSize        = 59
)

因此,在Node.js中哈希字符串与在Go中进行比较时可能会出现错误,其他语言也是如此。


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