使用PHP hash()和NodeJS crypto.createHash()比较SHA256,

我正在使用NodeJS为我的网站做一个实时应用程序,允许我的用户使用他们的账户login等等。

但是,我在logging部分问题。

当我在主站点上注册/login用户时,我使用PHP的hash()函数散列密码,如下所示:

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

而且在我的网站上效果很好
但是我需要能够从NodeJS的数据库中获取用户的salt,并且能够散列用户input的密码,并根据数据库的密码进行检查,并确保它们匹配以便将用户login。

我是这样做的:

 //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() hashedvariables和dbPasswordvariables,他们不相等 – 所以它总是返回false /响应不正确的密码。

所以,我的问题是:
有没有什么办法可以像在PHP中一样精确地在NodeJS中散列sha256string?

PS:现在我正在使用Ajax / jQuery通过PHP脚本进行login,但是我希望能够从Apache / PHP托pipe完全转移到仅由NodeJS(SocketIO,Express,MySQL)托pipe的站点。

我刚刚开始使用NodeJS,并在Apache网站上使用NodeJS的function,但是我听说使用NodeJS托pipe整个站点会更好/更高效。


编辑:所以,我决定做一个快速的test.js而不使用数据库/ socketio /快递。

 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)
密码salt存储在列中的是texttypes,并具有utf8_unicode_ci的sorting规则


注意:当我改变

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

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

哈希值是不同的,但hashedvariables与数据库密码仍然不匹配。

TL; DR

2种可能性:

  1. 理想的解决scheme:将salt的数据库字段从TEXT更改为BLOB
  2. 妥协:把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'); 

这是testing和工作。

更长的版本

当然,罪魁祸首是字符编码,真是一个惊喜。 另外一个可怕的select就是将原始二进制文件存储到TEXT字段。

那么,这是烦人的debugging。 所以,我设置了一个带有TEXT字段和一个BLOB字段的MySQL表,并在两者中存储了mcrypt_create_iv(100)的输出。 然后我从PHP和NodeJS做了相同的查询。

  • PHP将这两个值呈现为相同的。
  • JavaScript提供了2个不同的值。

在这两种情况下, BLOB都是准确的,我甚至设法通过使用input的所有3个组件的Buffer值在JavaScript下得到正确的散列。

但是这并不能解释为什么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 )上,这就是为什么两个值都在那里相同的原因。

出于某种原因,似乎在NodeJS的MySQL模块中设置charset不起作用,至less对我而言。 解决scheme是在现场级别进行转换,并通过转换为BINARY保存数据:

 BINARY(CONVERT(passwordSalt USING latin1)) as passwordSalt 

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

但是这还不够。 我们有一个string和二进制混合饲料哈希函数,我们需要巩固。 我们把密码和服务器的盐投给Buffer并连接:

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

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

虽然这可行,但最好的解决scheme仍然是在数据库中使用BLOB 。 在这两种情况下,铸造到Buffer是必要的。