Salt in PBKDF2 – Python

Question:

I’m just learning about securing password while developing using MySQL and Python, following this tutorial.

It’s my understanding that the userpassword is stored at the database hashed, and the salt is stored along side unencrypted, so that we can grab the hashed password and the salt, and rehash using the salt the inputted password, then compare the two.

Though, when using PBKDF2 (via the passlib.hash.sha256_crypt() function) I can’t set my own salt, only its size. So how can I rehash the password using the same salt so I can compare both?

Asked By: hiperbolt

||

Answers:

The Passlib Password Hash interface either lets you set the salt size, or the salt value itself. From the documentation on pbkdf2_sha256:

  • salt (bytes) Optional salt bytes. If specified, the length must be between 0-1024 bytes. If not specified, a 16 byte salt will be autogenerated (this is recommended).

  • salt_size (int) – Optional number of bytes to use when autogenerating new salts. Defaults to 16 bytes, but can be any value between 0 and 1024.

so you can set your own pre-generated salt:

>>> from passlib.hash import pbkdf2_sha256
>>> pbkdf2_sha256.hash("password", rounds=200000, salt=b'spamhameggs')
'$pbkdf2-sha256$200000$c3BhbWhhbWVnZ3M$WL9OLVcb3f7HqHeNT./kCJeunydLCi4JykzEuAdewcI'

However, note that the salt is part of the returned string. The string contains not only the resulting hash, but also the algorithm, the number of rounds used and the salt used, delimited by $. The salt is encoded with a modified form of base64. You can verify this by decoding the string c3BhbWhhbWVnZ3M again::

>>> from passlib.utils.binary import ab64_decode
>>> ab64_decode(b'c3BhbWhhbWVnZ3M')
b'spamhameggs'

See the Format & Algorithm section for the pbkdf2_sha256 docs.

So when you store the full string pbkdf2_sha256 in the database, everything to validate the string is right there in the value, including the salt. Leaving generating a random salt is best left to that library as it’ll use a secure method to generate one.

You may want to read the Passlib tutorial on password hashing, which includes coverage of how to hash passwords when storing in the database, and how to verify them again (e.g. using pdkdf2_sha256.verify(password_user_entered, hash_stored_in_database)), which covers exactly this ground.

Answered By: Martijn Pieters