I'm working on making a real-time application for my website in NodeJS, allowing my users to log in with their accounts, etc.
However, I am having some issues with the logging in part.
When I register/login the users on the main site, I hashed their password using PHP's hash() function like so:
$passwordSalt = mcrypt_create_iv(100);
$hashed = hash("sha256", $password.$passwordSalt.$serverSalt);
and it works great on my site
However I need to be able to grab the user's salt from the database in NodeJS and be able to hash the user's inputted password, check it against the database's password, and make sure they match to log the user in.
I do this by doing something like this:
//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;
}
However, when I console.log() the hashed variable and the dbPassword variable, they're not equal - so it always return false/responds with incorrect password.
So, my question:
Is there any way I can accurately hash a sha256 string in NodeJS the same way I do in PHP?
PS:
For now I am using Ajax/jQuery to login via PHP Script but I want to be able to completely move from Apache/PHP hosting to the site just being hosted with NodeJS (SocketIO, Express, MySQL).
I just started working with NodeJS recently and I have made functionality on my Apache site to work with NodeJS, but I heard that hosting the whole site itself using NodeJS would be a lot better/more efficient.
Edit:
So, I decided to make a quick test.js without using the database/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!");
}
As for how I'm connecting to the database, I'm doing so:
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 version: 5.5.44-0+deb7u1 (Debian)
The column the password salt is stored in is type of text, and has a collation of utf8_unicode_ci
Note: When I change
var hashed = crypto.createHash("sha256").update(checkPassword).digest('hex');
To:
var hashed = crypto.createHash("sha256").update(checkPassword, "utf8").digest('hex');
The hashes are different, however, the hashed variable doesn't match the database password still.
TL;DR
2 possibilities:
The ideal solution: change the database field for the salt from 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.
The longer version
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.
PHP presents both values as identical.
JavaScript presents 2 different values.
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.
The TEXT value had a length of 143 octets.
The 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.
Related
I would like to store user's profile data locally but would like to encrypt it using user's own ID and password. This way only when the same user logs back in can, she can decrypt her profile. Please find below steps I am using to encrypt on fetching data for first time and then decrypt on subsequent user login.
However, my attempt at it using code seen below is not working. I am able to encrypt and decrypt back same data. But decryption logic successfully returns data irrespective of which password I use.
The encryption algorithm arbitrarily chosen from the list available with Windows Runtime # https://msdn.microsoft.com/en-us/library/windows/apps/windows.security.cryptography.core.symmetrickeyalgorithmprovider.openalgorithm.aspx
Encryption
Encrypt user profile data using key generated using an serialized object containing User ID and password.
get buffer for original data
get buffer for serialized User ID and Password object
get a symmetric key using AES_ECB_PKCS7 algorithm
encrypt the buffered data using obtained symmetric key
var bufferResponseObj = Windows.Security.Cryptography.CryptographicBuffer.convertStringToBinary(data, Windows.Security.Cryptography.BinaryStringEncoding.utf8);
var bufferTokenObj = Windows.Security.Cryptography.CryptographicBuffer.convertStringToBinary(JSON.stringify({UID:"abc",PWD:"abc"}), Windows.Security.Cryptography.BinaryStringEncoding.utf8);
var cryptographicKey = Windows.Security.Cryptography.Core.SymmetricKeyAlgorithmProvider.openAlgorithm(Windows.Security.Cryptography.Core.SymmetricAlgorithmNames.aesEcbPkcs7).createSymmetricKey(bufferTokenObj);
var encryptedBufferData = Windows.Security.Cryptography.Core.CryptographicEngine.encrypt(cryptographicKey, bufferResponseObj, null);
var hexProtectedData = Windows.Security.Cryptography.CryptographicBuffer.encodeToHexString(encryptedBufferData);
Decryption
Decrypt the user profile data using key generated again using new object containing User ID and password.
get buffer for serialized User ID and Password object
get a new symmetric key again using AES_ECB_PKCS7 algorithm using
get buffer for encrypted data
decrypt the encrypted buffer data
var bufferTokenObj = Windows.Security.Cryptography.CryptographicBuffer.convertStringToBinary(JSON.stringify({UID:"abc",PWD:"def"}), Windows.Security.Cryptography.BinaryStringEncoding.utf8);
var cryptographicKey = Windows.Security.Cryptography.Core.SymmetricKeyAlgorithmProvider.openAlgorithm(Windows.Security.Cryptography.Core.SymmetricAlgorithmNames.aesEcbPkcs7).createSymmetricKey(bufferTokenObj);
var protectedData = Windows.Security.Cryptography.CryptographicBuffer.decodeFromHexString(encryptedData);
var decryptedBufferData = Windows.Security.Cryptography.Core.CryptographicEngine.decrypt(cryptographicKey, protectedData, null)
var decodedResponseObj = Windows.Security.Cryptography.CryptographicBuffer.convertBinaryToString(Windows.Security.Cryptography.BinaryStringEncoding.utf8, decryptedBufferData);
I am developing an application in which users are allowed o change their passwords.
I am using Node.js with mongoose and crypto.
In order to generate hashes for the passwords, I've hooked into model's pre('save') event. So basically when you use this:
var user = new User({password: '123'});
user.save(); // the password will be encrypted automatically
Having the ability to change the password, I need to find a way if the user has actually changed the password or modified any other fields. Example:
var user = User.findById('1234567', function(error, user){
user.name = 'Hello';
user.save(); // this will hash the password again, although it is not necessary
})
I am using Node.js's module called crypto to generate hash for passwords:
crypto.pbkdf2(password, salt, 5000, 512, 'sha512', function(error, buffer){
this.mixins.callback.call(callback, [error, buffer.toString('base64')]);
}.bind(this));
Is there a way to check if the password is already a hashed?
If not, I will generate the hash again.
Thanks
This is inherently bad approach. Just call the field password_hash and always pass hash explicitly. Otherwise you might accidentally begin to store unhashed password after some minor configuration change.
Also it's generally not possible to distinguish hash from unhashed string if you allow arbitrary strings as passwords.
Hashed string has a hash algorithm identifier prefix. You can validate by checking whether the string is starting with $2a$ or $2b$
Reference
Rigth now i'm creating my own website and there need to be a log in function. I've made a sign up page that retrieves data from the input fields and now comes the problem. I don't know how to save the passwords and username etc. Is it possible to save this into a text file and every time i open the website it retrieves this data? So, yes how do i do this and if this can't is there another way?
Here you see the code it retrieves. These variables need to be safed in a text file and retrieved next time website will be opend
var voornaam = document.getElementById("voorn").value;
var achternaam = document.getElementById("achtern").value;
var mail = document.getElementById("mail").value;
var password = document.getElementById("pass").value;
var gender = document.getElementById("iGender").value;
Solution:
Use a Database to save this information. Here a some Databases Mongo DB and Firebase
https://www.firebase.com/
Here's a course for Firebase http://www.codecademy.com/en/tracks/firebase
In addition to hashing the password, you probably want to be storing this information in a database. You will then have to add some logic to your application so that when a user logs in to your website, the values they enter into the log in form are checked against the values stored in the database when they signed up. In very general terms, that is probably the most standard architecture to get a log in functionality up and running.
You will have to write a service on your server that takes the form values, checks them against a database and then updates the HTML accordingly.
You could create a cookie and read it when the page loads: http://www.w3schools.com/js/js_cookies.asp
Create: document.cookie = "voornaam="+voornaam
Read: $(function(){ voornaam = document.cookie.replace("voornaam=",""); })
EDIT:
To save objects, you can use the JSON-library like so:
document.cookie = JSON.stringify({foo:"bar"}); cookieObject = JSON.parse(document.cookie);
But have in mind that the user can read and manipulate this cookie...
I have a simple little user registration form that looks like this:
// POST Register new user
exports.new = function(req, res) {
var db = require('mongojs').connect('localhost/busapp', ['users']);
db.users.ensureIndex({email:1}, {unique: true})
function User(email, username, password, dateCreated) {
this.email = email;
this.username = username;
this.password = password;
this.dateCreated = new Date();
this.admin = 0;
this.activated = 0
}
if (req.body.user.password !== req.body.user.passwordc) {
res.send('Passwords do not match');
} else {
var user = new User(req.body.user.email, req.body.user.username,
req.body.user.password);
// TODO: Remove this after we clarify that it works.
console.log(user.email + " " + user.username + " " +
user.password);
// Save user to database
db.users.save(user, function(err, savedUser) {
if (err) {
res.send(err);
} else {
console.log("User " + savedUser.email + " saved");
}
});
}
}
But I'm having trouble validating information submitted, like unique values, is empty, that sort of thing, so nobody can send post requests to the database to bypass the jQuery validation functions. I've read through the docs but I cannot seem to get it right. I tried setting a ensureIndex, but, that doesn't seem to work. Any information on how to validate the input on the database side would be great thanks!
One of the strengths/features of MongoDB is flexible schema. MongoDB does not impose any specific contraints on fields types. In general with web applications, you should try to do validation as early as possible .. so first at the client (JavaScript) level, then the application, and as a last resort in the database server.
MongoDB validation
MongoDB can do a limited amount of validation such as ensuring a unique index. Any data validation such as required fields or field types (string, integer, ..) should be done in your application code.
Clientside/application validation
You could use jQuery validation, but that would only be effective in the client (browser view). Any validation should also be done in your application code/model, otherwise disabling JavaScript in the browser would be a simple way to insert invalid data.
why cant you do stuff like password != "". as for unique values you should do use the find or findOne functions to see if that name exists in the db.
i would highly recommend installing mongoose. it is really useful as it allows you to create schemas. so if you are familiar with MVC, in your models, you would have user.js which contains the schema for the user. basically it gives guidelines on how the user object will be stored in the database. in your controllers, you would try to do what you are doing in the code you have above. you would do a user = require(user.js) and then you would do user.find() or user.findOne() to find that thing in the database. for example. if the username was already in the database, then its not unique. so dont add him.
I'm trying to use python / mechanize to login to this webpage:
http://www.solaradata.com/cgi-bin/mainProgram.cgi
The login form uses a Javascript function that produces an MD5 hash from several field values before submitting the results for authentication. Since mechanize can't do javascript, I tried to replicate the same functionality inside of python and then submit the resulting values. However, I'm still getting "invalid user / password" errors.
Here's my current code, can anybody point me towards where I went wrong? Thanks!
url_login = 'http://www.solaradata.com/cgi-bin/mainProgram.cgi'
import mechanize
import md5
username = 'superfly' #not my real user/pass
password = 'stickyguy' #not my real user/pass
br = mechanize.Browser()
br.open(url_login)
br.select_form(nr=0)
br.set_all_readonly(False)
session = br['session']
br['user'] = username
br['password'] = password
m1 = md5.new()
m1.update(password + username)
br['password'] = m1.digest()
m2 = md5.new()
m2.update(password + session)
br['hash'] = m2.digest()
for form in br.forms():
#print form
request2 = form.click() # mechanize.Request object
try:
response2 = mechanize.urlopen(request2)
except mechanize.HTTPError, response2:
pass
print response2.geturl()
# headers
for name, value in response2.info().items():
if name != "date":
print "%s: %s" % (name.title(), value)
print response2.read() # body
response2.close()
Use m1.hexdigest() instead of m1.digest()
I'm not familiar with python, but it appears the they are returning the hex value of the MD5 hash in the javascript version of the algorithm. Does the python MD5 do the same?
You should be able to test this without going through the submission process and testing for success. Instead, using a JavaScript developer tool such as Firebug or Chrome developer tools, calculate the result you get in-page. Then, using the same inputs, see what you get from your program. They should match, character for character.
It way be overkill but if you really need to script access a javascript heavy site you can look at selenium-rc or source labs.
These tools allow you to script an actual browser the same as a user.
Selenium