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

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

但是,我在记录中遇到了一些问题。

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

$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变量,他们不相等 - 所以它总是返回false /响应不正确的密码。

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

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

我刚刚开始使用NodeJS,并且在Apache网站上使用了NodeJS的功能,但是我听说使用NodeJS托管整个站点本身会更好/更高效。


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

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所存储的列是text类型,并具有utf8_unicode_ci的排序规则


注意:当我改变时

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

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

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


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都是准确的,我甚至设法通过使用输入的所有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模块中设置字符集不起作用,至少对我而言。 解决方案是在现场级别进行转换并通过转换为BINARY来保存数据:

    BINARY(CONVERT(passwordSalt USING latin1)) as passwordSalt
    

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

    但这还不够。 我们有混合的字符串和二进制来提供散列函数,我们需要巩固这一点。 我们将密码和服务器盐级联到Buffer和Concatenate:

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

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

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

    链接地址: http://www.djcxy.com/p/88125.html

    上一篇: Comparing SHA256 made with PHP hash() and NodeJS crypto.createHash()

    下一篇: Print newline in Fish (the programming language)?