The Passlib documentation has moved to https://passlib.readthedocs.io
passlib.hash.bcrypt
- BCrypt¶
BCrypt was developed to replace md5_crypt
for BSD systems.
It uses a modified version of the Blowfish stream cipher. Featuring
a large salt and variable number of rounds, it’s currently the default
password hash for many systems (notably BSD), and has no known weaknesses.
It is one of the four hashes Passlib recommends
for new applications. This class can be used directly as follows:
>>> from passlib.hash import bcrypt
>>> # generate new salt, hash password
>>> h = bcrypt.hash("password")
>>> h
'$2a$12$NT0I31Sa7ihGEWpka9ASYrEFkhuTNeBQ2xfZskIiiJeyFXhRgS.Sy'
>>> # the same, but with an explicit number of rounds
>>> bcrypt.using(rounds=8).hash("password")
'$2a$08$8wmNsdCH.M21f.LSBSnYjQrZ9l1EmtBc9uNPGL.9l75YE8D8FlnZC'
>>> # verify password
>>> bcrypt.verify("password", h)
True
>>> bcrypt.verify("wrong", h)
False
Note
It is strongly recommended that you install bcrypt when using this hash.
See also
the generic PasswordHash usage examples
Interface¶
-
class
passlib.hash.
bcrypt
¶ This class implements the BCrypt password hash, and follows the PasswordHash API.
It supports a fixed-length salt, and a variable number of rounds.
The
using()
method accepts the following optional keywords:Parameters: - salt (str) – Optional salt string.
If not specified, one will be autogenerated (this is recommended).
If specified, it must be 22 characters, drawn from the regexp range
[./0-9A-Za-z]
. - rounds (int) – Optional number of rounds to use.
Defaults to 12, must be between 4 and 31, inclusive.
This value is logarithmic, the actual number of iterations used will be
2**rounds
– increasing the rounds by +1 will double the amount of time taken. - ident (str) –
Specifies which version of the BCrypt algorithm will be used when creating a new hash. Typically this option is not needed, as the default (
"2b"
) is usually the correct choice. If specified, it must be one of the following:"2"
- the first revision of BCrypt, which suffers from a minor security flaw and is generally not used anymore."2a"
- some implementations suffered from rare security flaws, replaced by 2b."2y"
- format specific to the crypt_blowfish BCrypt implementation, identical to"2b"
in all but name."2b"
- latest revision of the official BCrypt algorithm, current default.
- truncate_error (bool) –
By default, BCrypt will silently truncate passwords larger than 72 bytes. Setting
truncate_error=True
will causehash()
to raise aPasswordTruncateError
instead.New in version 1.7.
- relaxed (bool) –
By default, providing an invalid value for one of the other keywords will result in a
ValueError
. Ifrelaxed=True
, and the error can be corrected, aPasslibHashWarning
will be issued instead. Correctable errors includerounds
that are too small or too large, andsalt
strings that are too long.New in version 1.6.
Changed in version 1.6: This class now supports
"2y"
hashes, and recognizes (but does not support) the broken"2x"
hashes. (see the crypt_blowfish bug for details).Changed in version 1.6: Added a pure-python backend.
Changed in version 1.6.3: Added support for
"2b"
variant.Changed in version 1.7: Now defaults to
"2b"
variant.- salt (str) – Optional salt string.
If not specified, one will be autogenerated (this is recommended).
If specified, it must be 22 characters, drawn from the regexp range
Bcrypt Backends¶
This class will use the first available of five possible backends:
- bcrypt, if installed.
- py-bcrypt, if installed.
- bcryptor, if installed.
- stdlib’s
crypt.crypt()
, if the host OS supports BCrypt (primarily BSD-derived systems). - A pure-python implementation of BCrypt, built into Passlib.
If no backends are available, hash()
and verify()
will throw MissingBackendError
when they are invoked.
You can check which backend is in use by calling bcrypt.get_backend()
.
As of Passlib 1.6.3, a one-time check is peformed when the backend is first loaded,
to detect the backend’s capabilities & bugs. If this check detects a fatal bug,
a PasslibSecurityError
will be raised. This generally means
you need to upgrade the external package being used as the backend
(this will be detailed in the error message).
Warning
The pure-python backend (#5) is disabled by default!
That backend is currently too slow to be usable given the number of rounds required
for security. That said, if you have no other alternative and need to use it,
set the environmental variable PASSLIB_BUILTIN_BCRYPT="enabled"
before importing Passlib.
What’s “too slow”? Passlib’s rounds selection guidelines currently require BCrypt be able to do at least 12 cost in under 300ms. By this standard the pure-python backend is 128x too slow under CPython 2.7, and 16x too slow under PyPy 1.8. (speedups are welcome!)
Format & Algorithm¶
Bcrypt is compatible with the Modular Crypt Format, and uses a number of identifying
prefixes: $2$
, $2a$
, $2x$
, $2y$
, and $2b$
. Each prefix indicates
a different revision of the BCrypt algorithm; and all but the $2b$
identifier are
considered deprecated.
An example hash (of password
) is:
$2b$12$GhvMmNVjRW29ulnudl.LbuAnUtN/LRfe1JsBm1Xu6LE3059z5Tr8m
Bcrypt hashes have the format $2a$rounds$saltchecksum
, where:
rounds
is a cost parameter, encoded as 2 zero-padded decimal digits, which determines the number of iterations used viaiterations=2**rounds
(rounds is 12 in the example).salt
is a 22 character salt string, using the characters in the regexp range[./A-Za-z0-9]
(GhvMmNVjRW29ulnudl.Lbu
in the example).checksum
is a 31 character checksum, using the same characters as the salt (AnUtN/LRfe1JsBm1Xu6LE3059z5Tr8m
in the example).
While BCrypt’s basic algorithm is described in its design document [1], the OpenBSD implementation [2] is considered the canonical reference, even though it differs from the design document in a few small ways.
Security Issues¶
Password Truncation.
While not a security issue per-se, bcrypt does have one major limitation: password are truncated on the first NULL byte (if any), and only the first 72 bytes of a password are hashed... all the rest are ignored. Furthermore, bytes 55-72 are not fully mixed into the resulting hash (citation needed!). To work around both these issues, many applications first run the password through a message digest such as SHA2-256. Passlib offers the premade passlib.hash.bcrypt_sha256 - BCrypt+SHA256 to take care of this issue.
Deviations¶
This implementation of bcrypt differs from others in a few ways:
Restricted salt string character set:
BCrypt does not specify what the behavior should be when passed a salt string outside of the regexp range
[./A-Za-z0-9]
. In order to avoid this situation, Passlib strictly limits salts to the allowed character set, and will throw aValueError
if an invalid salt character is encountered.Unicode Policy:
The underlying algorithm takes in a password specified as a series of non-null bytes, and does not specify what encoding should be used; though a
us-ascii
compatible encoding is implied by nearly all implementations of bcrypt as well as all known reference hashes.In order to provide support for unicode strings, Passlib will encode unicode passwords using
utf-8
before running them through bcrypt. If a different encoding is desired by an application, the password should be encoded before handing it to Passlib.Padding Bits
BCrypt’s base64 encoding results in the last character of the salt encoding only 2 bits of data, the remaining 4 are “padding” bits. Similarly, the last character of the digest contains 4 bits of data, and 2 padding bits. Because of the way they are coded, many BCrypt implementations will reject all passwords if these padding bits are not set to 0. Due to a legacy issue with Passlib <= 1.5.2, Passlib will print a warning if it encounters hashes with any padding bits set, and then validate the hash as if the padding bits were cleared. (This behavior will eventually be deprecated and such hashes will throw a
ValueError
instead).The crypt_blowfish 8-bit bug
Pre-1.1 versions of the crypt_blowfish bcrypt implementation suffered from a serious flaw [3] in how they handled 8-bit passwords. The manner in which the flaw was fixed resulted in crypt_blowfish adding support for two new BCrypt hash identifiers:
$2x$
, allowing sysadmins to mark any$2a$
hashes which were potentially generated with the buggy algorithm. Passlib 1.6 recognizes (but does not currently support generating or verifying) these hashes.$2y$
, the default for crypt_blowfish 1.1-1.2, indicates the hash was generated with the canonical OpenBSD-compatible algorithm, and should match correctly generated$2a$
hashes. Passlib 1.6 can generate and verify these hashes.As well, crypt_blowfish 1.2 modified the way it generates
$2a$
hashes, so that passwords containing the byte value 0xFF are hashed in a manner incompatible with either the buggy or canonical algorithms. Passlib does not support this algorithmic variant either, though it should be very rarely encountered in practice.(crypt_blowfish 1.3 switched to the
$2b$
standard as the default)Changed in version 1.6.3: Passlib will now throw a
PasslibSecurityError
if an attempt is made to use any backend which is vulnerable to this bug.The ‘BSD wraparound’ bug
OpenBSD <= 5.4, and most bcrypt libraries derived from it’s source, are vulnerable to a ‘wraparound’ bug [4], where passwords larger than 254 characters will be incorrectly hashed using only the first few characters of the string, resulting in a severely weakened hash.
OpenBSD 5.5 fixed this flaw, and introduced the
$2b$
hash identifier to indicate the hash was generated with the correct algorithm.py-bcrypt <= 0.4 is known to be vulnerable to this, as well as the os_crypt backend (if running on a vulnerable operating system).
Passlib 1.6.3 adds the following:
- Support for the
$2b$
hash format (though for backward compat it has not been made the default yet). - Detects if the active backend is vulnerable to the bug, issues a warning, and enables a workaround so that vulnerable passwords will still be hashed correctly. (This does mean that existing hashes suffering this vulnerability will no longer verify using their correct password).
- Support for the
Footnotes
[1] | the bcrypt format specification - http://www.usenix.org/event/usenix99/provos/provos_html/ |
[2] | the OpenBSD BCrypt source - http://www.openbsd.org/cgi-bin/cvsweb/src/lib/libc/crypt/bcrypt.c |
[3] | The flaw in pre-1.1 crypt_blowfish is described here - CVE-2011-2483 |
[4] | The wraparound flaw is described here - http://www.openwall.com/lists/oss-security/2012/01/02/4 |