How to use Bcrypt to encrypt passwords in Django

I am trying to use Bcrypt to encrypt passwords that users provide upon registration and then use Bcrypt to validate a password a user provides upon login against the hashed version stored in the database.

There is some pretty good documentation about how to install Bcrypt on via the Django docs, but they don’t actually show you how to use Bcrypt to hash passwords or use other commands.

Do you need to import Brcrypt from somewhere? If so, what is the correct syntax for it? What is the syntax for hashing passwords and comparing hashed passwords against non-hashed passwords?

I installed the Bcrypted library in the settings.py file and also installed Bcrypt via pip. What else do I need to do to use Bcrypt?

Best answer

At your link:

The password attribute of a User object is a string in this format:

<algorithm>$<iterations>$<salt>$<hash> Those are the components used
for storing a User’s password
, separated by the dollar-sign character
and consist of: the hashing algorithm, the number of algorithm
iterations (work factor), the random salt, and the resulting password
hash. The algorithm is one of a number of one-way hashing or password
storage algorithms Django can use; see below. Iterations describe the
number of times the algorithm is run over the hash. Salt is the random
seed used and the hash is the result of the one-way function.


I installed the Bcrypted library in the settings.py file…
What else do I need to do to use Bcrypt?

I’m not sure what that first sentence means. You need to put the following in settings.py:

PASSWORD_HASHERS = (
    'django.contrib.auth.hashers.BCryptSHA256PasswordHasher',
    'django.contrib.auth.hashers.BCryptPasswordHasher',
    'django.contrib.auth.hashers.PBKDF2PasswordHasher',
    'django.contrib.auth.hashers.PBKDF2SHA1PasswordHasher',
    'django.contrib.auth.hashers.SHA1PasswordHasher',
    'django.contrib.auth.hashers.MD5PasswordHasher',
    'django.contrib.auth.hashers.CryptPasswordHasher',
)

use Bcrypt to validate a password a user provides upon login against
the hashed version stored in the database.

You can do that manually:

The django.contrib.auth.hashers module provides a set of functions to
create and validate hashed password. You can use them independently
from the User model.

check_password(password, encoded)
If you’d like to manually authenticate a user by comparing a plain-text password to the hashed
password in the database, use the convenience function
check_password(). It takes two arguments: the plain-text password to
check, and the full value of a user’s password field in the database
to check against, and returns True if they match, False otherwise.

https://docs.djangoproject.com/en/1.9/topics/auth/passwords/#module-django.contrib.auth.hashers

Or, you can use authenticate():

authenticate(credentials)**
To authenticate a given username and password, use authenticate(). It takes credentials in the form of
keyword arguments, for the default configuration this is username and
password, and it returns a User object if the password is valid for
the given username. If the password is invalid, authenticate() returns
None. Example:

from django.contrib.auth import authenticate

user = authenticate(username='john', password='password to check')

if user is not None:
    # the password verified for the user
    if user.is_active:
        print("User is valid, active and authenticated")
    else:
        print("The password is valid, but the account has been disabled!")
else:
    # the authentication system was unable to verify the username and password
    print("The username and password were incorrect.")

https://docs.djangoproject.com/en/1.9/topics/auth/default/#authenticating-users

Here are some examples:

(django186p34)~/django_projects/dj1$ python manage.py shell

Python 3.4.3 (v3.4.3:9b73f1c3e601, Feb 23 2015, 02:52:03) 
[GCC 4.2.1 (Apple Inc. build 5666) (dot 3)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)

>>> from django.conf import settings
>>> print(settings.PASSWORD_HASHERS)

('django.contrib.auth.hashers.PBKDF2PasswordHasher',
 'django.contrib.auth.hashers.PBKDF2SHA1PasswordHasher',
 'django.contrib.auth.hashers.BCryptSHA256PasswordHasher',
 'django.contrib.auth.hashers.BCryptPasswordHasher',
 'django.contrib.auth.hashers.SHA1PasswordHasher',
 'django.contrib.auth.hashers.MD5PasswordHasher',
 'django.contrib.auth.hashers.UnsaltedSHA1PasswordHasher',
 'django.contrib.auth.hashers.UnsaltedMD5PasswordHasher', 
 'django.contrib.auth.hashers.CryptPasswordHasher')

Those are the defaults: there is no entry in my settings.py for PASSWORD_HASHERS.

>>> from django.contrib.auth.models import User

>>> my_user = User.objects.create_user('ea87', '[email protected]', '666monkeysAndDogs777')

>>> my_user.save()
>>> my_user.password
'pbkdf2_sha256$20000$L7uq6goI1HIl$RYqywMgPywhhku/YqIxWKbpxODBeczfLm5zthHjNSSk='
>>> my_user.username
'ea87'

>>> from django.contrib.auth import authenticate

>>> authenticate(username='ea87', password='666monkeysAndDogs777')
<User: ea87>

>>> print(authenticate(username='ea87', password='wrong password'))
None

>>> from django.contrib.auth.hashers import check_password

>>> check_password('666monkeysAndDogs777', my_user.password)
True

>>> exit()

Next, I added the following to settings.py:

PASSWORD_HASHERS = (
    'django.contrib.auth.hashers.BCryptSHA256PasswordHasher',
    'django.contrib.auth.hashers.BCryptPasswordHasher',
    'django.contrib.auth.hashers.PBKDF2PasswordHasher',
    'django.contrib.auth.hashers.PBKDF2SHA1PasswordHasher',
    'django.contrib.auth.hashers.SHA1PasswordHasher',
    'django.contrib.auth.hashers.MD5PasswordHasher',
    'django.contrib.auth.hashers.CryptPasswordHasher',
)

(django186p34)~/django_projects/dj1$ python manage.py shell

Python 3.4.3 (v3.4.3:9b73f1c3e601, Feb 23 2015, 02:52:03) 
[GCC 4.2.1 (Apple Inc. build 5666) (dot 3)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)

>>> from django.conf import settings
>>> print(settings.PASSWORD_HASHERS)
('django.contrib.auth.hashers.BCryptSHA256PasswordHasher',
 'django.contrib.auth.hashers.BCryptPasswordHasher',
 'django.contrib.auth.hashers.PBKDF2PasswordHasher',
 'django.contrib.auth.hashers.PBKDF2SHA1PasswordHasher',
 'django.contrib.auth.hashers.SHA1PasswordHasher',
 'django.contrib.auth.hashers.MD5PasswordHasher', 
 'django.contrib.auth.hashers.CryptPasswordHasher')

Note the bcrypt hashers at the front of the tuple.

>>> from django.contrib.auth.models import User

>>> user = User.objects.get(username='ea87')
>>> user
<User: ea87>

>>> user.password
'pbkdf2_sha256$20000$DS20ZOCWTBFN$AFfzg3iC24Pkj5UtEu3O+J8KOVBQvaLVx43D0Wsr4PY='

>>> user.set_password('666monkeysAndDogs777')
>>> user.password
'bcrypt_sha256$$2b$12$QeWvpi7hQ8cPQBF0LzD4C.89R81AV4PxK0kjVXG73fkLoQxYBundW'

You can see that the password has changed to a bcrypt version.