SALT and HASH password in nodejs w/ crypto - node.js

I am trying to figure out how to salt and hash a password in nodejs using the crypto module. I am able to create the hashed password doing this:
UserSchema.pre('save', function(next) {
var user = this;
var salt = crypto.randomBytes(128).toString('base64');
crypto.pbkdf2(user.password, salt, 10000, 512, function(err, derivedKey) {
user.password = derivedKey;
next();
});
});
However I am confused about how to later validate the password.
UserSchema.methods.validPassword = function(password) {
// need to salt and hash this password I think to compare
// how to I get the salt?
}

In whatever persistence mechanism (database) you're using, you would store the resulting hash alongside the salt and number of iterations, both of which would be plaintext. If each password uses different salt (which you should do), you must also save that information.
You would then compare the new plain text password, hash that using the same salt (and iterations), then compare the byte sequence with the stored one.
To generate the password (pseudo)
function hashPassword(password) {
var salt = crypto.randomBytes(128).toString('base64');
var iterations = 10000;
var hash = pbkdf2(password, salt, iterations);
return {
salt: salt,
hash: hash,
iterations: iterations
};
}
To validate password (pseudo)
function isPasswordCorrect(savedHash, savedSalt, savedIterations, passwordAttempt) {
return savedHash == pbkdf2(passwordAttempt, savedSalt, savedIterations);
}

Based on the nodejs documentation (http://nodejs.org/api/crypto.html), it doesn't look like there is a specific method that will validate a password for you. To validate it manually, you will need to compute the hash of the currently provided password and compare it to the stored one for equality. Basically, you will do the same thing with the challenge password that you did with the original, but use the salt stored in the database instead of generating a new one, and then compare the two hashes.
If you aren't too committed to using the built in crypto library, I might recommend using bcrypt instead. The two are about equal on the security front, but I think bcrypt has a more user-friendly interface. An example of how to use it (taken directly from the bcrypt docs on the page linked above) would be this:
Create a hash:
var bcrypt = require('bcrypt');
var salt = bcrypt.genSaltSync(10);
var hash = bcrypt.hashSync("B4c0/\/", salt);
// Store hash in your password DB.
To check a password:
// Load hash from your password DB.
bcrypt.compareSync("B4c0/\/", hash); // true
bcrypt.compareSync("not_bacon", hash); // false
Edit to add:
Another advantage of bcrypt is that the output of the genSalt function contains both the hash and the salt in one string. This means that you can store just the single item in your database, instead of two. There is also a method provided that will generate a salt at the same time that the hashing occurs, so you don't have to worry about managing the salt at all.
Edit to update:
In response to the comment from Peter Lyons: you're 100% correct. I had assumed that the bcrypt module that I had recommended was a javascript implementation, and therefor using it asynchronously wouldn't really speed things up on node's single threaded model. It turns out that this is not the case; the bcrypt module uses native c++ code for it's computations and will run faster asynchronously. Peter Lyons is right, you should use the asynchronous version of the method first and only pick the synchronous one when necessary. The asynchronous method might be as slow as the synchronous one, but the synchronous one will always be slow.

Either store password and salt in separate columns in your database, or (my preferred method), store your passwords in your database in a format that's compatible with RFC 2307 section 5.3. An example would be {X-PBKDF2}base64salt:base64digest. You could also store your iteration count in there, which allows you to increase the iteration count in the future for new accounts and accounts that update your passwords, without breaking logins for everyone else.
An example hash from my own PBKDF2 module for Perl looks like
{X-PBKDF2}HMACSHA1:AAAD6A:8ODUPA==:1HSdSVVwlWSZhbPGO7GIZ4iUbrk= which includes the specific hash algorithm used, as well as the number of iterations, the salt, and the resulting key.

This is a modified version of #Matthews answer, using TypeScript
import * as crypto from "crypto";
const PASSWORD_LENGTH = 256;
const SALT_LENGTH = 64;
const ITERATIONS = 10000;
const DIGEST = "sha256";
const BYTE_TO_STRING_ENCODING = "hex"; // this could be base64, for instance
/**
* The information about the password that is stored in the database
*/
interface PersistedPassword {
salt: string;
hash: string;
iterations: number;
}
/**
* Generates a PersistedPassword given the password provided by the user.
* This should be called when creating a user or redefining the password
*/
export function generateHashPassword(
password: string
): Promise<PersistedPassword> {
return new Promise<PersistedPassword>((accept, reject) => {
const salt = crypto
.randomBytes(SALT_LENGTH)
.toString(BYTE_TO_STRING_ENCODING);
crypto.pbkdf2(
password,
salt,
ITERATIONS,
PASSWORD_LENGTH,
DIGEST,
(error, hash) => {
if (error) {
return reject(error);
}
accept({
salt,
hash: hash.toString(BYTE_TO_STRING_ENCODING),
iterations: ITERATIONS,
});
}
);
});
}
/**
* Verifies the attempted password against the password information saved in
* the database. This should be called when
* the user tries to log in.
*/
export function verifyPassword(
persistedPassword: PersistedPassword,
passwordAttempt: string
): Promise<boolean> {
return new Promise<boolean>((accept, reject) => {
crypto.pbkdf2(
passwordAttempt,
persistedPassword.salt,
persistedPassword.iterations,
PASSWORD_LENGTH,
DIGEST,
(error, hash) => {
if (error) {
return reject(error);
}
accept(
persistedPassword.hash === hash.toString(BYTE_TO_STRING_ENCODING)
);
}
);
});
}

Faced with the same question I brought everything together into one module: https://www.npmjs.org/package/password-hash-and-salt
It uses pbkdf2 and stores hash, salt, algorithm, and iterations in a single field. Hope it helps.

There are two major steps involved in this scenario
1) Creating and Storing password
Here you will have to do the following.
Take the user password
Generate a string of random chars (salt)
Combine the salt with the user entered password
Hash the combined string.
Store the hash and the salt in the database.
2) Validating user password
This step would be required to authenticate the user.
The user will enter the username/email and the password.
Fetch the hash and the salt based on the username entered
Combine the salt with the user password
Hash the combination with the same hashing algorithm.
Compare the result.
This tutorial has a detailed explaination on how to do it with nodejs crypto. Exactly what you are looking for.
Salt Hash passwords using NodeJS crypto

Related

How to verify a hashed secret without knowing the salt?

I store the API-Keys as hashes in a database.
...
async function createToken(userId:number) {
...
const salt=await bcrypt.genSalt(15)
const hash=await bcrypt.hash(token, salt)
await db.store({userId,hash})
}
async function verifyToken(token:string){
const userId= //I don't know the UserID since only the token is sent as HTTP header
const hashes= db.get(userId)
for(const hash of hashes) {
if(await bcrypt.compare(token, hash)) {
return true
}
}
return false
}
How do I verify the token validity without knowing the userID?
The only option I see is to loop through all DB records, and try if the produced hash for that record matches. But this results potentially in thousands of hashes checked before I find the right one.
I may reduce this to only the tokens belonging to a specific UserID. However, this would require my users to also send there userID which I don't want them to do.
Seems like you are using bcrypt hashes as token to authenticate the users, but that's not the actual purpose of such hashes. If you need to use a token, why not use something like JWT. It can hold the user information, and can be verified without a single db call.

Decrypt the password in mongoose

How to decrypt any encrypted password in nodeJS using mongoose?
`bcrypt.genSalt(5, (err, Salt) => {
bcrypt.hash(this.password, Salt, (err, hash) => {
if(err) {
console.log('Error in generating salt: ' + err)
}
else {
this.password = hash
this.saltString = Salt
next()
}
})
})`
No, its not possible.
Bcrypt is one way hashing algorithm.
It's not possible to decrypt the password in bcrypt.
You can hash your password again with the same salt and compare the hashes.
TL;TR
No
Explaination
This password is hashed with a salt.
Hashes are made not to be decryptable.
There may even be multiple cleartexts(passwords) that give you the same hash(collisions).
Brute-force (try every possible password) won't help too because it takes very long(million years if you have a good password).
You may try to do a dictionary attack (try words from a dictionary with a few changes) but this won't work if the password is random or e.g. contains multiple/rare words).
In order to avoid that the same password for a username(or anything like that), salts are added that make decryption even more difficult.
Bcrypt can not be cracked until now.

Storing user-specific API keys in DB

So I am programming a timetable app in meteor, using the UNTIS backend. The problem I'm facing right now is, that I don't want each user to re-enter their passwords each time there is a request to the server. Sometimes I can't even (for example at 6 am to check if the first lesson isn't cancelled after all).
The problem is, that the password is needed in plain. So the password has to be accesible in plain at some point or another on the server.
The way I solved this problem:
I created a Meteor settings file:
/development.json
{
"ENCRYPT_PASSW_ENCRYPTION_KEY": "*some long encryption key*",
"ENCRYPT_PASSW_SALT_LENGTH": 32,
"ENCRYPT_PASSW_USER_KEY_LENGTH": 32,
"ENCRYPT_PBKDF2_ROUNDS": 100,
"ENCRYPT_PBKDF2_DIGEST": "sha512",
"ENCRYPT_PASSW_ALGORITHM": "aes-256-ctr"
}
In order to be able to use these settings in your meteor app, you will have to start meteor like this: meteor run --settings development.json
Sidenote: Of course you have to add your own parameters. These are just the development settings. You will have to choose your own parameters, depending on the importance of your data. (PBKDF2_ROUNDS should be chosen to fit your host system. I've read somewhere that a hash should take at least 241 milliseconds)
Some server side functions:
// server/lib/encryption.js
const crypto = require("crypto");
// generate a cryptograhpically secure salt
// with the length specified in the settings
generateUserSalt = function (length = Meteor.settings.ENCRYPT_PASSW_SALT_LENGTH) {
return crypto.randomBytes(length).toString("base64");
}
// encrypt a password with a key, derived from the
// application key plus the users salt
encryptUserPass = function (uid, pass, salt = false) {
const key = getUserKey(uid, salt),
algorithm = Meteor.settings.ENCRYPT_PASSW_ALGORITHM,
cipher = crypto.createCipher(algorithm, key);
return cipher.update(pass,'utf8','hex') + cipher.final('hex');
}
// decrypt a password with the same key
decryptUserPass = function (uid, ciphertext, salt = false) {
const key = getUserKey(uid, salt),
algorithm = Meteor.settings.ENCRYPT_PASSW_ALGORITHM,
decipher = crypto.createDecipher(algorithm, key);
return decipher.update(ciphertext,'hex','utf8') + decipher.final('utf8');
}
// generate the user-specific key that derives from
// the applications main encryption key plus the users
// specific salt. this is only needed in this scope
function getUserKey (uid, salt = false) {
// if no salt is given, take it from the user db
if (salt === false) {
const usr = Meteor.users.findOne(uid);
if (!usr || !usr.api_private || !usr.api_private.salt) {
throw new Meteor.Error("no-salt-given", "The salt from user with id" + uid + " couldn't be located. Maybe it's not set?");
}
salt = usr.untis_private.salt;
}
const systemKey = Meteor.settings.ENCRYPT_PASSW_ENCRYPTION_KEY,
rounds = Meteor.settings.ENCRYPT_PBKDF2_ROUNDS,
length = Meteor.settings.ENCRYPT_PASSW_USER_KEY_LENGTH,
digest = Meteor.settings.ENCRYPT_PBKDF2_DIGEST;
const userKey = crypto.pbkdf2Sync(systemKey, salt, rounds, length, digest);
return userKey.toString('hex');
}
And now I can encrypt each users password with a unique key like this:
// either with generating a salt (then the uid is not needed)
let salt = generateUserSalt(),
encPass = encryptUserPass(0, pass, salt);
// or when the user already has a salt (salt is in db)
let encPass2 = encryptUserPass(uid, pass);
Decrypting is also verry easy:
let pass = decryptUserPass(0, passEnc, salt);
// or
let pass2 = decryptUserPass(uid, passEnc);
Explanation
Of course I know, that this is still pretty bad in terms of security (storing things on the server that can be reversed into user's passwords). The reason why I think this is okay:
Each users password is encrypted like this:
AES(password, PBKDF2(global-encryption-key + salt))
This means that:
each user's password is encrypted with a different key
no encryption keys are saved in the db
Why I think this is a good sollution:
In case the database is leaked, the attacker will firstly need to guess the AES key correctly for one specific user and then reverse the PBKDF2 to find the global-encryption-key. or
Guess the global-encryption-key
Therefore you should chose a rather large global-encryption-key.
Facts about salts
NEVER use a salt twice
Change salts together with passwords (don't reuse them)
Salts should be long: rule of thumb: make the salt as long the output of the hash function (sha256 = 32 bytes)
more about salting things

Passport bcrypt password is different than password from db

In my local-register I store my user data in the database. To hash the password I use bcrypt:
console.log(password);
password = bcrypt.hashSync(password);
console.log(password);
If I sign up a user with the password stackoverflow the password looks like this:
stackoverflow
$2a$10$uoJH1Wo9b7SQploRptfODe1Q2kRC3skQoUNOIhAmHg2AWykWQwGvW
When I log in a user stackoverflow#stackoverflow.com with the password stackoverflow
var hashedpassword = bcrypt.hashSync(password);
console.log(password);
console.log(hashedpassword);
stackoverflow
$2a$10$aq869JEMWBQ8vCfXfuRvlOPdUvq.UhTz4Ge.kB3n7wSyvhjBsm8r2
So even though I use the same bcrypt module the hash is different every time I log in.
I don't understand the inner workings of bcrypt, but the hash may look different for the same string each time because the salt is generated as part of the hashing. Thus, you can't do:
stored = hash(old);
guess = hash(guess);
valid = stored == guess;
Instead you have to use the compare method, as in:
/* password is *not* hashed! */
bcrypt.compare(password, usersHashedPassword, cb);

How does node.bcrypt.js compare hashed and plaintext passwords without the salt?

From github:
To hash a password:
var bcrypt = require('bcrypt');
bcrypt.genSalt(10, function(err, salt) {
bcrypt.hash("B4c0/\/", salt, function(err, hash) {
// Store hash in your password DB.
});
});
To check a password:
// Load hash from your password DB.
bcrypt.compare("B4c0/\/", hash, function(err, res) {
// res == true
});
bcrypt.compare("not_bacon", hash, function(err, res) {
// res = false
});
From above, how can there be no salt values involved in the comparisons? What am I missing here?
The salt is incorporated into the hash (as plaintext). The compare function simply pulls the salt out of the hash and then uses it to hash the password and perform the comparison.
Bcrypt compare hashed and plaintext passwords without the salt string because the hashed password contains the salt string which we created at the time of hashing.
For example :
Take this Plain Password :
546456546456546456456546111
Hashed Password of above plain text using Bcrypt :
$2b$10$uuIKmW3Pvme9tH8qOn/H7uZqlv9ENS7zlIbkMvCSDIv7aup3WNH9W
So in the above hashed password, There are three fields delimited by $ symbol.
i) First Part $2b$ identifies the bcrypt algorithm version used.
ii) Second Part $10$ 10 is the cost factor (nothing but salt rounds while we creating the salt string. If we do 15 rounds, then the value will be $15$
iii) Third Part is first 22 characters (that is nothing but salt string)
In this case it is
uuIKmW3Pvme9tH8qOn/H7u
The remaining string is hashed password.
So basically, the saltedHash = salt string + hashedPassword to protect from rainbow table attacks.
I had the same question too as the original poster and it took a look bit of looking around and trying different things to understand the mechanism. As has already been pointed out by others, the salt is concatenated to the final hash. So this means a couple of things:
The algorithm must know the length of the salt
Must also know the position of the salt in the final string. e.g. if offset by a specific number from left or right.
These two things are usually hard coded in the implementation e.g. the bcrypt implementation source for bcryptjs defines the salt length as 16
/**
* #type {number}
* #const
* #private
*/
var BCRYPT_SALT_LEN = 16;
So to illustrate the basic concept behind the idea if one wanted to do it manually, It would look similar to the below. I do not recommend implementing stuff like this yourself when there are libraries that you can get to do it.
var salt_length = 16;
var salt_offset = 0;
var genSalt = function(callback)
{
var alphaNum = '0123456789abcdefghijklmnopqurstuvwxyzABCDEFGHIJKLMNOPQURSTUVWXYZ';
var salt = '';
for (var i = 0; i < salt_length; i++) {
var j = Math.floor(Math.random() * alphaNum.length);
salt += alphaNum[j];
}
callback(salt);
}
// cryptographic hash function of your choice e.g. shar2
// preferably included from an External Library (dont reinvent the wheel)
var shar2 = function(str) {
// shar2 logic here
// return hashed string;
}
var hash = function(passwordText, callback)
{
var passwordHash = null;
genSalt(function(salt){
passwordHash = salt + shar2(passwordText + salt);
});
callback(null, passwordHash);
}
var compare = function(passwordText, passwordHash, callback)
{
var salt = passwordHash.substr(salt_offset, salt_length);
validatedHash = salt + shar2(passwordText + salt);
callback(passwordHash === validatedHash);
}
// sample usage
var encryptPassword = function(user)
{
// user is an object with fields like username, pass, email
hash(user.pass, function(err, passwordHash){
// use the hashed password here
user.pass = passwordHash;
});
return user;
}
var checkPassword = function(passwordText, user)
{
// user has been returned from database with a hashed password
compare(passwordText, user.pass, function(result){
// result will be true if the two are equal
if (result){
// succeeded
console.log('Correct Password');
}
else {
// failed
console.log('Incorrect Password');
}
});
}
Because I had the same question myself, I know exactly what you are thinking about.
You have a misconception between "Secret Key" which is used in Cryptographic algorithms and "Salt" which is used to slow down the encryption process and make it harder for hackers to use brute force.
When you use the plain password and the salt to generate the hash, this hash uses as secret key the password itself! So the next time you will try to compare it with a plain password, this plain password must be the exact same one you used to generate the hash! So this is why you don't have to store it somewhere else because it is always provided by the user on both register and login steps!
It is just a fixed length string.
console.log("");
var salt = bcrypt.genSaltSync(10);
console.log(salt);
hash = bcrypt.hashSync("foobar", salt);
console.log(hash);
console.log("");
var salt = bcrypt.genSaltSync(10);
console.log(salt);
hash = bcrypt.hashSync("foobar", salt);
console.log(hash);
console.log("");
var salt = bcrypt.genSaltSync(10);
console.log(salt);
hash = bcrypt.hashSync("foobar", salt);
console.log(hash);
$2a$10$onmcKV.USxnoQAsQwBFB3e
$2a$10$onmcKV.USxnoQAsQwBFB3eytL3UZvZ5v/SudaWyaB9Vuq9buUqGO2
$2a$10$mwQfdyVS9dsO4SuxoR5Ime
$2a$10$mwQfdyVS9dsO4SuxoR5ImeG7atz7RXGRXb.c0VHp5zSn1N2VOA.Vq
$2a$10$uVUuJr6LryjchhKEg6PH7u
$2a$10$uVUuJr6LryjchhKEg6PH7unTw8aJGK0i3266c5kqDBLJkf80RHEpq
$2a$10$Y.upG5/54zvJyZacRxP17O
$2a$10$Y.upG5/54zvJyZacRxP17OH60BC0hQRMNfQjJxSWE77fyBrbzalmS
The salt is incorporated into the hash. The compare function simply pulls the salt out of the hash and then uses it to hash the password and perform the comparison.
When a user will log into our system, we should check the password entered is correct or not. Unlike other systems that would decrypt the password in the database (if it is encrypted), and compare it with the one entered by the user, what I do with bcrypt ( given it implements one-way hashing) is encrypt the one entered by the user. To do this, I will pass the password to bcrypt to calculate the hash, but also the password stored in the database associated with the user (hash). This is because, as mentioned before, the bcrypt algorithm used a random segment (salt) to generate the hash associated with the pasword. This was stored along with the password, and you need it to recalculate the hash of the password entered by the user and finally compare with the one entered when registering and see if they match.

Resources