Do I Need to Store the Salt with Bcrypt

How can bcrypt have built-in salts?

This is bcrypt:

Generate a random salt. A "cost" factor has been pre-configured. Collect a password.

Derive an encryption key from the password using the salt and cost factor. Use it to encrypt a well-known string. Store the cost, salt, and cipher text. Because these three elements have a known length, it's easy to concatenate them and store them in a single field, yet be able to split them apart later.

When someone tries to authenticate, retrieve the stored cost and salt. Derive a key from the input password, cost and salt. Encrypt the same well-known string. If the generated cipher text matches the stored cipher text, the password is a match.

Bcrypt operates in a very similar manner to more traditional schemes based on algorithms like PBKDF2. The main difference is its use of a derived key to encrypt known plain text; other schemes (reasonably) assume the key derivation function is irreversible, and store the derived key directly.


Stored in the database, a bcrypt "hash" might look something like this:

$2a$10$vI8aWBnW3fID.ZQ4/zo1G.q1lRps.9cGLcZEiGDMVr5yUP1KUOYTa

This is actually three fields, delimited by "$":

  • 2a identifies the bcrypt algorithm version that was used.
  • 10 is the cost factor; 210 iterations of the key derivation function are used (which is not enough, by the way. I'd recommend a cost of 12 or more.)
  • vI8aWBnW3fID.ZQ4/zo1G.q1lRps.9cGLcZEiGDMVr5yUP1KUOYTa is the salt and the cipher text, concatenated and encoded in a modified Base-64. The first 22 characters decode to a 16-byte value for the salt. The remaining characters are cipher text to be compared for authentication.

This example is taken from the documentation for Coda Hale's ruby implementation.

Do I need to store the salt with bcrypt?

The salt is incorporated into the hash (encoded in a base64-style format).

For example, in traditional Unix passwords the salt was stored as the first two characters of the password. The remaining characters represented the hash value. The checker function knows this, and pulls the hash apart to get the salt back out.

BCrypt. How to store salt with python3?

Update

As of version 3.1.0 bcrypt provides the convenience function

checkpw(password, hashed_password)

to perform password checking against a hashed password. This should be used instead of:

bcrypt.hashpw(passwd_to_check, hashed_passwd) == hashed_passwd

which is shown below. There is still no need to store the hash separately.


First of all, you don't need to store the salt because it is part of the hash produced by bcrypt.hashpw(). You just need to store the hash. E.g.

>>> salt = bcrypt.gensalt()
>>> salt
b'$2b$12$ge7ZjwywBd5r5KG.tcznne'
>>> passwd = b'p@ssw0rd'
>>> hashed_passwd = bcrypt.hashpw(passwd, salt)
b'$2b$12$ge7ZjwywBd5r5KG.tcznnez8pEYcE1QvKshpqh3rrmwNTQIaDWWvO'
>>> hashed_passwd.startswith(salt)
True

So you can see that the salt is included in the hash.

You can also use bcrypt.hashpw() to check that a password matches a hashed password:

>>> passwd_to_check = b'p@ssw0rd'
>>> matched = bcrypt.hashpw(passwd_to_check, hashed_passwd) == hashed_passwd
>>> matched
True
>>> bcrypt.hashpw(b'thewrongpassword', hashed_passwd) == hashed_passwd
False

No need to store the salt separately.


So you could write the setter like this (Python 3):

@password.setter
def password(self, passwd):
if isinstance(passwd, str):
passwd = bytes(passwd, 'utf-8')
self.passwd = str(bcrypt.hashpw(passwd, bcrypt.gensalt()), 'utf8')

And the checker like this:

def check_password(self, passwd_to_check):
if isinstance(passwd_to_check, str):
passwd_to_check = bytes(passwd_to_check, 'utf-8')
passwd = bytes(self.passwd, 'utf8')
return bcrypt.hashpw(passwd_to_check, passwd) == passwd

Do we need to use a fixed salt with BCrypt?

As you wrote, the salt prevents rainbow table attacks and doesn't help against brute-forcing, it's BCrypt's slowness which mitigates brute-forcing. BCrypt offers a cost factor, which controls the time necessary to calculate a single hash.

The additional protection you want can be achieved better by encrypting the calculated hash with a server side key (any algorithm like AES-256). The key doesn't become part of the hash then and can be exchanged whenever this is necessary. The advantage is the same as with your fixed salt (actually called pepper), only an attacker with privileges on the server can start cracking the password hashes. I tried to explain this at the end of my tutorial about safely storing passwords.

So let the salt do its job and do not mix it up with other tasks, instead encrypt the password-hashes afterwards.

Why is BCrypt more secure than just storing a salt and an encrypted password in the database?

You can't just hash a password, if you do that it will be vulnerable to dictionary attacks; therefore you salt the password before hashing it; this is what BCrypt does.

Password salts can be public, however they must be unique for each password. The point of them is to prevent dictionary attacks on hashes (so you can't look through a list of premade hashes which corresponds to passwords).

Like PBKDF2, Bcrypt is an adaptive function; you can increase iterations later on to make the hash less vulnerable to brute force attacks as more computing power comes out. Despite this Bcrypt is harder to accelerate on GPUs than PBKDF2.

Why isn't salt required to compare whether password is correct in bcrypt?

The salt is part of the string bcrypt stores in the database, see for instance the answer on Do I need to store the salt with bcrypt?

Where do you store your salt strings?

The point of rainbow tables is that they're created in advance and distributed en masse to save calculation time for others - it takes just as long to generate rainbow tables on the fly as it would to just crack the password+salt combination directly (since effectively what's being done when generating rainbow tables is pre-running the calculations for brute-forcing the hash), thus the argument that by knowing the salt someone could "generate a rainbow table" is spurious.

There's no real point in storing salts in a separate file as long as they're on a per-user basis - the point of the salt is simply to make it so that one rainbow table can't break every password in the DB.

What are Salt Rounds and how are Salts stored in Bcrypt?

  1. With "salt round" they actually mean the cost factor. The cost factor controls how much time is needed to calculate a single BCrypt hash. The higher the cost factor, the more hashing rounds are done. Increasing the cost factor by 1 doubles the necessary time. The more time is necessary, the more difficult is brute-forcing.
  2. The salt is a random value, and should differ for each calculation, so the result should hardly ever be the same, even for equal passwords.
  3. The salt is usually included in the resulting hash-string in readable form. So with storing the hash-string you also store the salt. Have a look at this answer for more details.

The point with the salt in bcrypt

The idea behind a salt is that even if two inputs are the same, the hash will not be identical as long as a different salt is used every time.

For example, many users pick the same password. If you just store the hash of the password, the database will contain many identical hashes - so that if an attacker finds the password just once, he can then use it for all those users easily. However, if the password is hashed with a different salt value for each user, the attacker will have to crack each and every hash stored in the store.

I'm not sure what's that code you're using (what's that crypt function?), but it's ok if it prepends the salt value to the actual hash as long as the hash itself is also calculated using the salt. You're going to need to store the original salt anyway to verify that a new input (password) matches the stored hash. However, as long as you change the salt values between every hash usage, there's no easy way to glean information about the original input.



Related Topics



Leave a reply



Submit