7

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?

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
hiperbolt
  • 184
  • 2
  • 3
  • 11

1 Answers1

9

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.

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
  • How secure is passlib? Perhaps I'm a bid paranoid, but are there standard libraries that do that? Maintained by Python devs? – PythonForEver Feb 24 '23 at 15:01
  • 1
    @Riya: it's a long-running, popular Open Source library to handle this task, and as far as I know, the defacto library for this in the Python ecosystem. I don't think you can get more 'standard' than this. – Martijn Pieters Feb 27 '23 at 10:34
  • 1
    @Riya: searching on GitHub I found ~40k mentions in [`requirements.txt`](https://github.com/search?q=passlib+filename%requirements.txt&type=code), [`setup.cfg`](https://github.com/search?q=passlib+filename%3Asetup.cfg&type=code), [`pyproject.toml`](https://github.com/search?q=passlib+filename%3Apyproject.toml&type=code) and [`Pipfile`](https://github.com/search?q=passlib+filename%3APipfile&type=code) dependency files. – Martijn Pieters Feb 27 '23 at 10:38
  • 1
    @Riya: last but not least, it's the de-facto password hashing library for most Python web frameworks; e.g. the official FastAPI tutorial [references it](https://fastapi.tiangolo.com/tutorial/security/oauth2-jwt/#install-passlib), and while Flask has several packages handling user logins, at least 3 of them use passlib for hashing (Flask[`-User`](https://flask-user.readthedocs.io/en/latest/), [`-Security`](https://pythonhosted.org/Flask-Security/) and [`-Security-Too`](https://flask-security-too.readthedocs.io/en/stable/)). – Martijn Pieters Feb 27 '23 at 10:45