I've created a registration web page and I'm wondering what's the best way to upload password to server and how to store user passwords.
Currently, I just have a field for password and when the user clicks "submit", it makes a call to the server then insert the password into the db as is.
My question is at which step should I encrypt the password and what are some good methods of keeping this data secure?
I've looked around but haven't found much resource on this, maybe I'm not searching the right things.
The popular way of doing this in node is using bcrypt password encoder.
const bcrypt = require('bcrypt');
const saltRounds = 10;
const myPlaintextPassword = 's0/\/\P4$$w0rD';
const someOtherPlaintextPassword = 'not_bacon';
// hashing
bcrypt.hash(myPlaintextPassword, saltRounds, function(err, hash) {
// Store hash in your password DB.
});
// comparing
bcrypt.compare(myPlaintextPassword, hash, function(err, result) {
// result == true
});
Related
Ive implemented a password authentication with passport and local authentication in Node.js that is as follows:
userSchema.pre('save', function (next) {
if (this.password) {
this.salt =
Buffer.from(crypto.randomBytes(16).toString('base64'), 'base64');
this.password = this.hashPassword(this.password);
}
next();
});
userSchema.methods.hashPassword = function (password) {
return crypto.pbkdf2Sync(password, this.salt, 10000,
64, 'sha512').toString('base64');
};
Now I added a pre hook for updating the password where I create a new salt and hash the new password. The update itself is executed, but the resulting new salt and password hash fail the password check.
I added this code (which is a bit raw, but for now I would be happy if the concept works):
userSchema.pre('findOneAndUpdate', function (next) {
if (this._update.password) {
this.salt =
Buffer.from(crypto.randomBytes(16).toString('base64'), 'base64');
this._update.salt = this.salt;
this._update.password = crypto.pbkdf2Sync(this._update.password, this.salt, 10000,64,'sha512').toString('base64');
}
next();
});
The update is done with this code:
users.findOneAndUpdate({email: req.body.emailadres },
{password: req.body.password}).exec();
I see the salt and password values being updated in the collection, but the authentication returns a failure on login after the update.
I'd expect that the update of the fields as in the above code should work. Am i missing something? Also, how is the hashPassword reusable for the update hook?
First of all, this answer is not what I'm looking for.
I'm adding a DB to the educational project. For the previous one, I've just queried the DB with MongoDB.MongoClient and got/set the records with MongoDB.MongoClient.collection.insertOne.
For example, adding user is as simple as:
mongodb.MongoClient.collection("users").insertOne({
username,
password // jsut a sha512 password, which is sent hashed from UI
})
But as far as I understood, the right way(a better way) is to create a Mongoose Schema and add function to it. But the password creation seems way trickier than expected. Why is that a difference? If a password is stolen from UI, it doesn't matter how many encryptions used. However, if the password is simply hashed, it doesn't matter if you steal it from back-end - it's impossible to trigger that endpoint with an already hashed password.
const UserSchema = new mongoose.Schema({
username: {
type: String,
lowercase: true,
unique: true,
index: true,
required: [true, 'username can\'t be blank'],
match: [/^[a-zA-Z0-9]+$/, 'username is invalid'],
},
salt: String,
hash: String,
});
UserSchema.plugin(uniqueValidator, {message: 'is already taken.'});
UserSchema.methods.setPassword = function(password) {
this.salt = crypto.randomBytes(16).toString('hex');
this.hash = crypto.pbkdf2Sync(password, this.salt, 10000, 512, 'sha512').toString('hex');
};
Basically, the question is:
Which way is better and is any of the ways imply, that I have to use that huge encryption?
As far as I understand, it's possible to have 1 single file with mongodb.collection(*) functions, or dozens of schemas. Why the answer is still not obvious?
Mongoose using mongodb driver under the hood
It is not good practice to put cpu/memory heavy tasks on client side
Nor mongoose not mongodb native driver forcing you to use anything, wether to hash password or not (and how heavy encryption should be used) is totally up to you.
So basically I would recommend you to make use of native mongo driver, use just any pretty simple hashing from nodejs docs and just add hashing on user creation before you make insert:
const hash = (password) => {
const hash = crypto.createHash('sha256');
hash.update(password);
return hash.digest('hex');
}
...
app.post('/user', async (req, res) => {
try {
// validation of user credentials here
const userToInsert = { ...req.body, password: hash(req.body.password) };
await mongodb.MongoClient.collection("users").insertOne(userToInsert);
res.status(201).send("done");
} catch (err) {
console.error(err);
res.status(500).send('internal server error');
}
})
Guys I have some general questions about interacting with my PostgreSQL DB my ExpressJS API and my React Native Frontend.
I managed to write some queries that for example check if the username that is entered in my RN app matches the one that is on the DB and returns its userID.
I know that on my DB every User has its Password that is being stored inside the DB in a hashed way.
How would a Query look like that would return for example the userID as I am doing it but only if the username exists and it matches the Password that is stored on the DB?
Do I need to know how the passwords are hashed on the DB?
The Query for userid:
static getUserId (username, callback) {
db.query(`SELECT userid FROM employee WHERE username = '${username}'`, (err, res)=> {
if(err.error){
return callback(err.error)
} else {
return callback(res)
}
})
}
The passwords are saved in the same employee table under password.
EDIT: I think I can take two WHERE statements like this:
`SELECT userid FROM employee WHERE username = '${username}' AND WHERE password = '${password}'`
but if a User enters his password inside the app it is not hashed so do I need to send the same hash method to compare these values?
Thanks in advance!
Best regards Faded.
You can use bcrypt.js for hashing your passwords.
To hash password in SignUp.js:
app.post('/signup',(req,res,next)=> {
//Password from request body of register request
const myPlaintextPassword = req.body.password
//saltRounds are typically set to 12
bcrypt.hash(myPlaintextPassword, saltRounds, function(err, hash) {
// Store hash in your password DB with INSERT.
});
}
Now to compare with Hash in Login.js:
app.post('/login',async (req,res,next)=>{
const existingUser = await db.query(`SELECT userid FROM employee WHERE username = '${username}'`, (err, res)=> {
if(err.error){
return callback(err.error)
} else {
return callback(res)
}
})
const valid = await bcrypt.compare(req.body.password, existingUser.password);
if (!valid) {
res.status(401).send('Wrong pass');
return req.next()
}
}
You need to hash the incoming password and compare the two hashes.
That being said: if this is going to be at all used by real people I would suggest using a library to handle your auth instead of writing your own. There are a lot of things that can go wrong to make your application insecure.
I am using the following code to hash (and hopefully salt) user passwords before I store them in my DB.
// hash the password before the user is saved
ConsultantSchema.pre('save', function(next) {
var user = this;
// hash the password only if the password has been changed or user is new
if (!user.isModified('password')) return next();
// generate the hash
bcrypt.hash(user.password, null, null, function(err, hash) {
if (err) {
logger.error("bcrypt.hash "+err);
return next(err);
}
// change the password to the hashed version
user.password = hash;
next();
});
});
What I am confused about, is the part
bcrypt.hash(user.password, null, null, function(err, hash) {
I got this code from a tutorial and I have seen it quite often searching for an answer.
Based on the documentation (https://www.npmjs.com/package/bcrypt) for bcrypt I would have expected the following code
const saltrounds = 10;
bcrypt.hash(user.password, saltRounds, function(err, hash) {
To be working but this breaks my program without an error.
My questions are:
Why are there two "null" arguments? What are they for?
Is the hash salted based on the code with the two nulls?
Thank you in advance for you help!
There is a difference between bcrypt and bcrypt-nodejs. The following code is from their docs at npmjs.com.
bcrypt hashing
bcrypt.hash(myPlaintextPassword, salt, function(err, hash)
or
bcrypt.hash(myPlaintextPassword, saltRounds, function(err, hash)
bcrypt-nodejs hashing
bcrypt.hash(myPlaintextPassword, null, null, function(err, hash)
Explanation
You are looking at the docs for bcrypt, not bcrypt-nodejs. If you are using node.js, you'll most likely want to use bcrypt-nodejs. I have multiple projects utilizing its features. The two null fields are for the salt and progress:
salt - [REQUIRED] - the salt to be used to hash the password.
progress - a callback to be called during the hash calculation to signify progress
I have used crypto library for hashing and it works great. Here is my code snippet
var salt = crypto.randomBytes(128).toString('base64');
var iterations = 10;
var keylen = 20;
crypto.pbkdf2(args.password, salt, iterations, keylen, function(succes, bcryptedPassword) {
console.log(bcryptedPassword.toString());
//Do actions here
});
Please check if it helps you or not
The following syntax is from the (abandoned?) bcrypt-nodejs module 1
bcrypt.hash(user.password, null, null, function(err, hash) {
You refer to the docs of the bcrypt module 2.
Make sure you're using the right module.
I am trying to store passwords and salts in MongoDB and I'm not sure which datatype should be used. When I use strings, the encrypted password appears to be stored correctly, but the generated salt, which is created with new Buffer(crypto.randomBytes(16).toString('base64'), 'base64');, seems to have characters that weren't recognized. For example, I have a salt stored as �y_�6j(�l~Z}0ۡ\" and I don't think this is correct.
Is the problem that it's stored as a string?
While registering a user, you can generate a hashed password using bcrypt. Let's call this password as P#1. Save this hashed password (P#1) in your database only, and not the salt.
While logging in a user, generate hashed version of the password which the user sends, let's call it P#2. Now you just have to match P# and P#2. If they match, the user is authenticated. This way you can perform authentication without actually saving the salt in your database.
I will try to put it in simple way with the help of an example.
// My user schema
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var bcrypt = require('bcrypt-nodejs');
var userSchema = new Schema({
username: String,
password: String
});
// hash the password
userSchema.methods.generateHash = function(password) {
return bcrypt.hashSync(password, bcrypt.genSaltSync(8), null);
};
// checking if password is valid
userSchema.methods.validPassword = function(password) {
return bcrypt.compareSync(password, this.password);
};
var User = mongoose.model('user', userSchema);
module.exports = User;
// My APIs for registering and authenticating a user
var User = require('/path/to/user/model');
app.post('/register', function(req, res) {
var new_user = new User({
username: req.body.username
});
new_user.password = new_user.generateHash(req.body.password);
new_user.save();
});
app.post('/login', function(req, res) {
User.findOne({username: req.body.username}, function(err, user) {
if (!user.validPassword(req.body.password)) {
//password did not match
} else {
// password matched. proceed forward
}
});
});
Hope it helps you!
Ankit Gomkale's answer is correct (and IMHO clean!), but you might wonder how it's possible to verify the hashed password is the same as the input string being tested.
This is because the output of bcrypt.hashSync(password, bcrypt.genSaltSync(8), null); is not a "hashed password", it is a "hash string", of the form (source):
$2a$10$N9qo8uLOickgx2ZMRZoMyeIjZAgcfl7p92ldGxad68LJZdL17lhWy
\__/\/ \____________________/\_____________________________/
Alg Cost Salt Hash
Therefore, this string contains the password hash, but also the salt. Verification of a password (e.g. in bcrypt.compareSync(password, this.password);) will use this salt, not create a new random salt.