The Passlib documentation has moved to

Frequently Asked Questions

This sections documents some frequently asked questions about Passlib, and password hashing in general. But it also includes some common misconceptions, as well as some esoteric and infrequently asked questions.

  • Calling PasswordHash.hash() multiple times for the same input generates a different result! What’s going on?

    For all the hashes which include a salt, PasswordHash.hash() will automatically generate a new one each time it’s invoked. Thus the salt & digest portions of the resulting hash string will be different every time.

  • Do I need to provide a salt each time PasswordHash.hash() is called, and store the salt separately in my database?

    No. Nearly all of the hash classes passlib.hash which use a salt will automatically generate a salt, and include it as part of the hash that’s returned.

    There are just a few hashes which require an external salt (like a username), or don’t contain a salt at all. These generally aren’t secure, and shouldn’t be used in unless you already know why you need to use them.

  • How do I decrypt the hashes generated by Passlib?

    Short answer: You can’t.

    Long answer:

    The hash algorithms in Passlib were explicitly designed so they are as hard to reverse as possible: you can hash a password, you can check if a password matches an existing hash, and that’s it. Unless it’s an ancient algorithm whose security has been fundamentally undermined, the only way to reverse a hash is to use brute force: hash all potential passwords until one matches. To fight this, one of the main goals of password hashing is to make this search take as long as possible.

    However, if you really need it, there are programs dedicated to this task, two prominent ones include John the Ripper and HashCat.

    There is one single decryptable hash in Passlib: cisco_type7, which was deliberately designed this way; and Passlib’s implementation offers a convenient decrypt() method.

  • Why use PasswordHash.verify() instead of hashing user input and using == to compare it with the stored hash?

    There are two reasons for this: One, PasswordHash.verify() uses a “constant time” equality check internally, which mitigates a class of timing attacks that == is potentially vulnerable to. These attacks are mostly theoretical for modern password hashes with a sufficient sized-salt, but it’s better to be safe than sorry.

    Two, many hash string formats encode a number of configuration parameters, some unhelpfully allow multiple encodings of the same parameters. Thus, to make sure passwords are hashed correctly for comparison, you’ll have to parse the hash string and pass the configuration parameters in yourself. PasswordHash.verify() takes care of this transparently.

  • Is SHA256-Crypt the same as SHA256? Is MD5-Crypt the same as MD5?

    No. MD5 and SHA256 are cryptographic hash functions, which whereas md5_crypt and sha256_crypt are complex password hash algorithms, containing a randomly generated salt, variable rounds, etc. They derive their names from the fact that they use the respective hash functions internally.