比较用 PHP hash() 和 NodeJS crypto.createHash() 制作的 SHA256

Posted

技术标签:

【中文标题】比较用 PHP hash() 和 NodeJS crypto.createHash() 制作的 SHA256【英文标题】:Comparing SHA256 made with PHP hash() and NodeJS crypto.createHash() 【发布时间】:2016-01-02 18:28:36 【问题描述】:

我正在用 NodeJS 为我的网站制作一个实时应用程序,允许我的用户使用他们的帐户登录等。

但是,我在部分日志记录方面遇到了一些问题。

当我在主站点上注册/登录用户时,我使用 phphash() 函数对他们的密码进行哈希处理,如下所示:

$passwordSalt = mcrypt_create_iv(100);
$hashed = hash("sha256", $password.$passwordSalt.$serverSalt);

它在我的网站上效果很好 但是,我需要能够在 NodeJS 中从数据库中获取用户的 salt,并能够对用户输入的密码进行哈希处理,将其与数据库的密码进行检查,并确保它们与用户登录匹配。

我通过这样做来做到这一点:

//Check if Username exists, then grab the password salt and password
//Hash the inputted password with the salt in the database for that user
//and the salt I used for $serverSalt in PHP when creating passwords
//check if hashed result in NodeJS is equal to the database password
function checkPass(dbPassword, password, dbSalt)
    var serverSalt = "mysupersecureserversalt";
    var hashed = crypto.createHash("sha256").update(password+dbSalt+serverSalt).digest('hex');
    if(hashed === dbPassword)
        return true;
    return false;

但是,当我 console.log() hashed 变量和 dbPassword 变量时,它们并不相等 - 所以它总是返回错误/响应错误的密码。

所以,我的问题: 有什么方法可以像在 PHP 中一样准确地在 NodeJS 中散列一个 sha256 字符串?

PS: 现在我使用 Ajax/jQuery 通过 PHP 脚本登录,但我希望能够完全从 Apache/PHP 托管转移到仅由 NodeJS(SocketIO、Express、mysql)托管的站点。

我最近才开始使用 NodeJS,并且我已经在我的 Apache 网站上制作了与 NodeJS 一起使用的功能,但我听说使用 NodeJS 托管整个网站本身会更好/更有效。


编辑: 因此,我决定在不使用 database/socketio/express 的情况下制作一个快速的 test.js。
var crypto = require("crypto");
var serverSalt = "";
var passwordSalt = "­"; //The salt directly copied from database
var checkPassword = "password123"+passwordSalt+serverSalt; //All added together
var password = ""; //The hashed password from database
var hashed = crypto.createHash("sha256").update(checkPassword).digest('hex');
console.log(password);
console.log(hashed); //This doesn't match the hash in the database
if(password == hashed)
        console.log("Logged in!");
 else 
        console.log("Error logging in!");


至于我是如何连接数据库的,我是这样做的:

  connection.query("SELECT password,passwordSalt FROM users WHERE username = "+connection.escape(data.username), function(err,res)
    if(err)console.log(err.stack);socket.emit("message", msg:"There was an error logging you in!", mType:"error");else
      if(res.length != 0)
        var dbSalt = res[0]['passwordSalt'];
        var serverSalt = ""; //My server salt
        var dbPassword = res[0]['password'];
        var checkPassword = data.password+dbSalt+serverSalt;
        console.log("CheckPass: "+checkPassword);
        var hashed = crypto.createHash("sha256").update(checkPassword).digest('hex');
        console.log("Hashed: "+hashed);
        if(hashed === dbPassword)
          console.log("Worked!");
          socket.emit("message", msg: "Logged in!", type:"success");
         else 
          console.log("Error logging in!");
          socket.emit("message", msg: "Your password is incorrect!", type:"error");
        
       else 
        socket.emit("message", msg: "That user ("+data.username+") doesn't exist!", mType:"error");
      
    
  );

MySQL 版本:5.5.44-0+deb7u1 (Debian) 存储密码盐的列的类型为text,排序规则为utf8_unicode_ci


注意:当我改变

var hashed = crypto.createHash("sha256").update(checkPassword).digest('hex'); 致:

var hashed = crypto.createHash("sha256").update(checkPassword, "utf8").digest('hex');

哈希值不同,但是hashed 变量仍然与数据库密码不匹配。

【问题讨论】:

在我的测试中,只要 password == $passworddbSalt == $passwordSaleserverSalt == $serverSalt 然后 php hash("sha256", ...) 产生与节点 crypto.createHash("sha256").update(...).digest('hex') 完全相同的输出 - 注意 createHash 不是散列 @JaromandaX 奇怪.. 每次我测试它时,它看起来都相似,但哈希值不同......难道是 $passwordSalt 搞砸了?也许当它被放入mysql数据库时?我正在做的是检查用户名是否在数据库中,如果是,它从数据库中获取密码盐(这是在为该用户创建密码时创建的),并用它对输入的密码进行哈希处理,并进行比较密码与用户的 我不知道是什么搞砸了,但是 php hash 与节点 crypto.createHash 相同......但是,我不知道 crypto.hash 是什么,所以这可能是什么你需要看看 你能显示用于检索数据的代码吗?盐存储在什么样的数据库字段中?你的 MySQL 版本是多少? 【参考方案1】:

TL;DR

2 种可能性:

    理想的解决方案:将 salt 的数据库字段从 TEXT 更改为 BLOB

    折衷方案:将TEXT 转换为二进制 latin1 使用:

    BINARY(CONVERT(passwordSalt USING latin1)) as passwordSalt
    

那么在这两种情况下,在任何地方都使用Buffer 值:

var hashed = crypto.createHash("sha256").update(
    Buffer.concat([
        new Buffer(password),
        dbSalt, // already a buffer
        new Buffer(serverSalt)
    ])
).digest('hex');

它已经过测试并且可以正常工作。

加长版

当然,罪魁祸首是字符编码,真是令人惊讶。将原始二进制文件存储到 TEXT 字段也是一个糟糕的选择。

嗯,调试起来很烦人。因此,我设置了一个带有 TEXT 字段和 BLOB 字段的 MySQL 表,并将 mcrypt_create_iv(100) 的输出存储在两者中。然后我从 PHP 和 NodeJS 进行了相同的查询。

PHP 将两个值显示为相同。 javascript 提供 2 个不同的值。

在这两种情况下,BLOB 都是准确的,我什至设法在 JavaScript 下通过对输入的所有 3 个组件使用 Buffer 值来获得正确的哈希值。

但这并不能解释为什么 PHP 和 JavaScript 会看到 TEXT 字段的 2 个不同值。

TEXT 值的长度为 143 个八位字节。 BLOB 的长度为 100 个八位字节。

显然BLOB 是正确的,而TEXT 不是,但 PHP 似乎并没有被这种差异所困扰。

如果我们查看PHP下的MySQL连接状态:

$mysqli->get_charset();

部分输出:

[charset]   => latin1
[collation] => latin1_swedish_ci

确实不足为奇,众所周知,PHP 默认在 ISO-8859-1 下运行(或 MySQL 中的 latin1),这就是为什么两个值在那里相同的原因。

出于某种原因,在 MySQL 模块中为 NodeJS 设置字符集似乎不起作用,至少对我来说是这样。解决方案是在字段级别进行转换并通过转换为 BINARY 来保留数据:

BINARY(CONVERT(passwordSalt USING latin1)) as passwordSalt

这返回与BLOB 完全相同的输出。

但这还不够。我们混合了字符串和二进制文件来提供给散列函数,我们需要整合它。我们将密码和服务器盐转换为Buffer 并连接:

var hashed = crypto.createHash("sha256").update(
    Buffer.concat([
        new Buffer(password),
        dbSalt, // already a buffer
        new Buffer(serverSalt)
    ])
).digest('hex');

这将返回与 PHP 相同的输出。

虽然这可行,但最好的解决方案仍然是在数据库中使用BLOB。在这两种情况下,都需要强制转换为 Buffer

【讨论】:

以上是关于比较用 PHP hash() 和 NodeJS crypto.createHash() 制作的 SHA256的主要内容,如果未能解决你的问题,请参考以下文章

NodeJS Crypto TripleDes 解密(mcrypt 端口)

node.js比php会不会取代php?

使用nodeJS的 crypto模块来为你的密码hash加盐

14.4 跨域 - hash(比较恶心 不用)

2017年的golangpythonphpc++cjavaNodejs性能对比(golang python php c++ java Nodejs Performance)

用 bcrypt (PHP) 比较两个哈希值