Bcrypt in Python — Complete Tutorial with Flask & Django
Learn how to hash and verify passwords with bcrypt in Python. Covers the bcrypt library, Flask-Bcrypt, Django password hashing, and security best practices.
Install bcrypt
pip install bcrypt
For Django, bcrypt is already supported — you just need to install it and configure it. For Flask, install flask-bcrypt:
pip install flask-bcrypt
Basic Usage
import bcrypt
# Hash a password
password = b'mySecurePassword123!' # Must be bytes
salt = bcrypt.gensalt(rounds=12)
hashed = bcrypt.hashpw(password, salt)
print(hashed)
# b'$2b$12$LQv3c1yqBWVHxkd0LHAkCO...'
# Verify a password
is_match = bcrypt.checkpw(password, hashed)
print(is_match) # True
is_wrong = bcrypt.checkpw(b'wrongPassword', hashed)
print(is_wrong) # False
Encoding Passwords
Python bcrypt requires bytes. If you receive a string from a form, encode it first:
import bcrypt
def hash_password(password: str) -> bytes:
"""Hash a plain-text password."""
password_bytes = password.encode('utf-8')
salt = bcrypt.gensalt(rounds=12)
return bcrypt.hashpw(password_bytes, salt)
def verify_password(password: str, hashed: bytes) -> bool:
"""Verify a password against its hash."""
password_bytes = password.encode('utf-8')
return bcrypt.checkpw(password_bytes, hashed)
Configurable Rounds
import os
import bcrypt
BCRYPT_ROUNDS = int(os.environ.get('BCRYPT_ROUNDS', 12))
def hash_password(password: str) -> bytes:
password_bytes = password.encode('utf-8')
salt = bcrypt.gensalt(rounds=BCRYPT_ROUNDS)
return bcrypt.hashpw(password_bytes, salt)
Flask Integration
Using flask-bcrypt:
from flask import Flask, request, jsonify
from flask_bcrypt import Bcrypt
import os
app = Flask(__name__)
bcrypt = Bcrypt(app)
# Set rounds in config
app.config['BCRYPT_LOG_ROUNDS'] = int(os.environ.get('BCRYPT_ROUNDS', 12))
# Simulate a database
users = {}
@app.route('/register', methods=['POST'])
def register():
data = request.get_json()
email = data.get('email')
password = data.get('password')
if not email or not password:
return jsonify({'error': 'Email and password required'}), 400
if len(password) < 8:
return jsonify({'error': 'Password must be at least 8 characters'}), 400
if email in users:
return jsonify({'error': 'Email already registered'}), 409
hashed = bcrypt.generate_password_hash(password).decode('utf-8')
users[email] = {'email': email, 'hash': hashed}
return jsonify({'message': 'User registered successfully'}), 201
@app.route('/login', methods=['POST'])
def login():
data = request.get_json()
email = data.get('email')
password = data.get('password')
user = users.get(email)
if not user or not bcrypt.check_password_hash(user['hash'], password):
return jsonify({'error': 'Invalid credentials'}), 401
return jsonify({'message': 'Login successful'})
Django Configuration
Django supports bcrypt natively. Configure it in settings.py:
# settings.py
PASSWORD_HASHERS = [
'django.contrib.auth.hashers.BCryptSHA256PasswordHasher', # First = default
'django.contrib.auth.hashers.PBKDF2PasswordHasher', # Fallback
'django.contrib.auth.hashers.PBKDF2SHA1PasswordHasher',
]
Django then handles everything automatically through User.set_password() and User.check_password():
from django.contrib.auth.models import User
# Create user with bcrypt hash
user = User.objects.create_user(
username='john',
email='john@example.com',
password='mySecurePassword123!' # Hashed automatically
)
# Verify password
is_valid = user.check_password('mySecurePassword123!') # True
Django — Custom Cost Factor
# settings.py
from django.contrib.auth.hashers import BCryptSHA256PasswordHasher
class CustomBcryptHasher(BCryptSHA256PasswordHasher):
rounds = 12 # Override the default
PASSWORD_HASHERS = [
'myapp.hashers.CustomBcryptHasher',
]
Or use BCRYPT_COST_FACTOR if your version supports it:
BCRYPT_COST_FACTOR = 12
Async Usage (FastAPI)
import asyncio
import bcrypt
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
app = FastAPI()
class UserCredentials(BaseModel):
email: str
password: str
async def hash_password_async(password: str) -> bytes:
loop = asyncio.get_event_loop()
password_bytes = password.encode('utf-8')
salt = bcrypt.gensalt(rounds=12)
return await loop.run_in_executor(None, bcrypt.hashpw, password_bytes, salt)
async def verify_password_async(password: str, hashed: bytes) -> bool:
loop = asyncio.get_event_loop()
password_bytes = password.encode('utf-8')
return await loop.run_in_executor(None, bcrypt.checkpw, password_bytes, hashed)
@app.post('/register')
async def register(credentials: UserCredentials):
hashed = await hash_password_async(credentials.password)
# Store hashed in database...
return {'message': 'User registered'}
run_in_executor is important — bcrypt.hashpw is blocking, and calling it directly in an async endpoint would block the event loop.
Common Mistakes
Forgetting to encode strings to bytes:
# ❌ Wrong
bcrypt.hashpw('password', salt) # TypeError
# ✅ Correct
bcrypt.hashpw('password'.encode('utf-8'), salt)
Using low rounds in production:
# ❌ Wrong
salt = bcrypt.gensalt(rounds=4) # Testing only
# ✅ Correct
salt = bcrypt.gensalt(rounds=12)
Comparing strings instead of using checkpw:
# ❌ Wrong — not timing-attack safe
if stored_hash == bcrypt.hashpw(password.encode(), stored_hash):
...
# ✅ Correct
if bcrypt.checkpw(password.encode(), stored_hash):
...
Summary
- Use
bcrypt.gensalt(rounds=12)minimum for 2026 - Always encode passwords to bytes with
.encode('utf-8') - Use
bcrypt.checkpw()for verification (timing-safe) - In async frameworks, run bcrypt in a thread executor
- In Django, configure
BCryptSHA256PasswordHasherinPASSWORD_HASHERS
Try our Bcrypt Generator to test hashes, or read our guide on choosing the right number of rounds.
Ready to try it?
Open Bcrypt Generator →Related Articles
How Many Bcrypt Rounds Should You Use in 2026?
A practical guide to choosing the right bcrypt cost factor. OWASP 2026 recommendations, performance benchmarks, and how to pick the right number of rounds for your application.
Bcrypt vs Argon2: Which Should You Use in 2026?
A detailed comparison of bcrypt and Argon2 for password hashing. Learn the differences, OWASP 2026 recommendations, and when to choose each algorithm.
Bcrypt in PHP — password_hash() & password_verify() Guide
Learn how to hash and verify passwords with bcrypt in PHP using password_hash() and password_verify(). Covers Laravel, migration from MD5, and best practices.