我正在为NodeJS中的网站制作实时应用程序,允许我的用户使用他们的帐户登录等。
但是,我在登录时遇到了一些问题。
当我在主站点注册/登录用户时,我使用PHP的hash()
函数对密码进行了哈希处理:
$passwordSalt = mcrypt_create_iv(100);
$hashed = hash("sha256", $password.$passwordSalt.$serverSalt);
它在我的网站上运行良好 但是我需要能够从NodeJS中的数据库中获取用户的盐,并能够对用户输入的密码进行哈希处理,并根据数据库的密码进行检查,并确保它们匹配记录用户。
我通过做这样的事情来做到这一点:
//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托管整个站点本身会更好/更高效。
<小时/> 修改: 所以,我决定不使用数据库/ 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)
存储密码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
变量仍与数据库密码不匹配。
答案 0 :(得分:4)
2 possibilities:
TEXT
to BLOB
.The compromise: cast the TEXT
to binary latin1 using:
BINARY(CONVERT(passwordSalt USING latin1)) as passwordSalt
Then in both cases, use Buffer
values everywhere:
var hashed = crypto.createHash("sha256").update(
Buffer.concat([
new Buffer(password),
dbSalt, // already a buffer
new Buffer(serverSalt)
])
).digest('hex');
It's tested and working.
And of course the culprit is character encoding, what a surprise. Also a terrible choice on your part to store raw binary to a TEXT
field.
Well, that was annoying to debug. So, I set up a MySQL table with a TEXT
field and a BLOB
field and stored the output of mcrypt_create_iv(100)
in both. Then I made the same queries from both PHP and NodeJS.
In both cases, the BLOB
was accurate and I even managed to get the proper hash under JavaScript by using Buffer
values for all 3 components of the input.
But this did not explain why PHP and JavaScript were seeing 2 differents values for the TEXT
field.
TEXT
value had a length of 143 octets.BLOB
had a length of 100 octets.Clearly the BLOB
was correct and the TEXT
wasn't, yet PHP didn't seem bothered by the difference.
If we look at the MySQL connection status under PHP:
$mysqli->get_charset();
Partial output:
[charset] => latin1
[collation] => latin1_swedish_ci
Unsurprising really, it is notorious that PHP operates under ISO-8859-1
by default (or latin1
in MySQL), which is why both values where the same there.
For some reason, it seems that setting the charset in the MySQL module for NodeJS doesn't work, at least for me. The solution was to convert at the field level and preserve the data by casting to BINARY
:
BINARY(CONVERT(passwordSalt USING latin1)) as passwordSalt
This returned exactly the same output as the BLOB
.
But this is not enough yet. We have a mixture of strings and binary to feed to the hashing function, we need to consolidate that. We cast the password and the server salt to Buffer
and concatenate:
var hashed = crypto.createHash("sha256").update(
Buffer.concat([
new Buffer(password),
dbSalt, // already a buffer
new Buffer(serverSalt)
])
).digest('hex');
This returns the same output as PHP.
While this works, the best solution is still to use BLOB
in the database. In both cases, casting to Buffer
is necessary.