121

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?

AJ.
  • 1,146
  • 11
  • 33
SChang
  • 1,669
  • 2
  • 13
  • 13

6 Answers6

121

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.

Bill
  • 25,119
  • 8
  • 94
  • 125
  • 5
    I still don't understand. During the compare, how does it know which part of the hash is the salt if you do not provide it with the salt? – MondayPaper May 22 '14 at 20:02
  • 10
    bcrypt is a standard and always concatenates the salt with the hash in the same format. You provide the salt when you encrypt and this gets incorporated into the hash. bcrypt will only be able to decrypt data that was originally encrypted using bcrypt, otherwise, you're right - there would be no way for it to know which part is the hash and which part is the salt. – Bill May 22 '14 at 20:50
  • @Bill, to my knowledge, hash is a one way encryption. So you can't and shouldn't decrypt. – AndaluZ Aug 29 '16 at 14:43
  • I guess you don't decrypt, but encrypt a plaintext with the salt and compare it to the already encrypted one. – ilyavf Nov 08 '16 at 03:59
  • 11
    Ok, we get it: salt is stored with the hash. bcrypt is open source, so this means everyone knows how exactly it stores it. So you know how to extract it, or how to generate hash from plain text password. How does this help to protect passwords against scanning rainbow tables for hashes, which is basically the main idea behind salt? – Vitalii Lebediev Nov 11 '16 at 23:23
  • +1 to @VitaliyLebedev I am puzzled with this as well. This is against the main idea of using salt value. The salt should be stored secretly and the compare should consume salt value as one of the parameters. – A-letubby Jan 31 '17 at 11:34
  • 24
    It doesn't matter if the attacker knows the salt for any particular hash, it's not a secret. Using a different salt for each password means the attacker can't precompute hashes using common values. With a different salt on each one, they would need to recompute any tables for every password which makes them useless. – Bill Feb 03 '17 at 06:12
  • 8
    look at this way, does it matter if attacker knows salt for certain user in data base like this: `column_password = hash, column_salt = salt` vs `column_password = hash_salt`. attacker still has same information. Point of salt is to make every password so random and bigger that it becomes unlikely someone has precomputed it. – Muhammad Umer Jun 04 '17 at 21:22
  • Years later, here. But i’m trying to understand this, too. So please correct me if I’m wrong. But as far as I understand it, bcrypt is a one-way algorithm. Meaning you can’t use the salt to decrypt a hash. Also, as far as I understand it, the compare function is using the salt to hash the newly submitted password and checking if the resulting hash is the same. Please set me straight if I am incorrect about this. I’m trying to learn it all. – Euroclydon37 Nov 19 '19 at 15:02
  • Thanks. It was very confusing. – Vahid Najafi Dec 11 '20 at 12:22
39

Bcrypt compares 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 the plain text above 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 the salt rounds used while creating the salt string) If we do 15 rounds, then the value will be $15$

III) Third part is the first 22 characters which are the salt string. In this case it is

uuIKmW3Pvme9tH8qOn/H7u

The remaining string is the hashed password - Zqlv9ENS7zlIbkMvCSDIv7aup3WNH9W

So basically, the saltedHash = salt string + hashedPassword to protect from rainbow table attacks.

Shape
  • 410
  • 1
  • 3
  • 21
Prash
  • 491
  • 4
  • 2
30

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:

  1. The algorithm must know the length of the salt
  2. 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');
        }
    });
}
Alappin
  • 674
  • 8
  • 9
5

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!

babaliaris
  • 663
  • 8
  • 16
4

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
sfy
  • 2,810
  • 1
  • 20
  • 22
1

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.

Kumar Ajay
  • 222
  • 4
  • 10