Django by Example: Password Hashing
Django never stores passwords in plain text. It uses the PBKDF2 algorithm with a SHA256 hash by default. This example shows how to manually hash passwords and check them, which is useful for custom user creation flows.
Code
from django.contrib.auth.hashers import make_password, check_password
# 1. Hashing a password (e.g., during Sign Up)
raw_password = "MySecretPassword123!"
hashed_password = make_password(raw_password)
print(hashed_password)
# Output looks like:
# pbkdf2_sha256$600000$HqX...$2sF...
# algorithm$iterations$salt$hash
# 2. Verifying a password (e.g., custom login)
is_correct = check_password("MySecretPassword123!", hashed_password)
print(f"Password matches: {is_correct}") # True
is_wrong = check_password("WrongPass", hashed_password)
print(f"Password matches: {is_wrong}") # False
# 3. Creating a user manually (Best Practice)
from django.contrib.auth.models import User
# DON'T do this:
# User.objects.create(username='u', password='raw_password')
# This stores plain text!
# DO this:
user = User.objects.create_user(
username='john',
email='[email protected]',
password='raw_password' # create_user handles hashing automatically
)
# OR
user = User(username='jane')
user.set_password('raw_password') # Hashes it
user.save()Explanation
Security is a core tenet of Django, and nowhere is this more evident than in password handling. Django never stores passwords in plain text. Instead, it uses a one-way cryptographic hash function. By default, this is PBKDF2 with a SHA256 digest, a robust algorithm designed to be computationally expensive to slow down brute-force attacks.
The hash format algorithm$iterations$salt$hash includes everything needed to verify the password later. The salt is a random string added to the password before hashing to prevent rainbow table attacks. The iterations (work factor) determine how many times the hash function is applied; Django increases this default over time as hardware becomes faster.
When creating users programmatically, you must use User.objects.create_user() (which calls make_password() internally) rather than User.objects.create(). The latter would store the raw password string in the database, leaving your users vulnerable. check_password() is the safe way to verify credentials, handling the salt and hashing algorithm automatically.
Code Breakdown
make_password(). Takes a plain string and returns the long hash string. It generates a random salt automatically every time it runs.check_password(). You cannot "decrypt" a hash. Instead, this function hashes the input password using the same salt found in the stored hash and compares the results.create_user(). This helper method is the standard way to create users. It ensures the password is hashed before saving to the DB.user.set_password(). If you are updating a password (e.g., "Change Password" form), use this method on the user instance, followed by user.save().
