Skip to content

Password HashingΒΆ

🎯 What You’ll LearnΒΆ

  • Why password hashing is essential for security
  • How hashing differs from encryption
  • How bcrypt protects against brute-force attacks
  • How to implement password hashing and verification in FastAPI using Passlib

πŸ” Why Hash Passwords?ΒΆ

When users sign up, they provide a password. If you store that password directly (plaintext), anyone with access to your database can see it β€” including attackers.

Hashing solves this by transforming the password into a one-way, irreversible string. Even if the database is leaked, the original password cannot be recovered.

πŸ” Hashing vs EncryptionΒΆ

Feature Hashing Encryption
Direction One-way Two-way (encrypt/decrypt)
Purpose Verify identity Protect readable data
Reversible ❌ No βœ… Yes
Use case Passwords Messages, files, tokens

Hashing is ideal for passwords because you never need to decrypt β€” you only need to compare.


🧠 Why bcrypt?¢

bcrypt is a hashing algorithm designed specifically for passwords. It’s slow and salted by default, which makes it resistant to:

  • Brute-force attacks: Trying millions of passwords per second
  • Rainbow table attacks: Precomputed hash lookups

bcrypt ensures that:

  • Identical passwords produce different hashes (thanks to salt)
  • Hashing takes time, making mass guessing impractical

πŸ› οΈ Step-by-Step: Implementing bcrypt in FastAPIΒΆ

πŸ“¦ Step 1: Install Passlib with bcrypt supportΒΆ

pip install passlib[bcrypt]

Passlib is a Python library that wraps bcrypt and other secure hashing algorithms.


πŸ“„ Step 2: Create a Password Utility ModuleΒΆ

πŸ“„ core/security.py

from passlib.context import CryptContext

# Create a password hashing context
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")

def hash_password(password: str) -> str:
    """Hash a plaintext password using bcrypt."""
    return pwd_context.hash(password)

def verify_password(plain_password: str, hashed_password: str) -> bool:
    """Verify a plaintext password against a hashed password."""
    return pwd_context.verify(plain_password, hashed_password)

πŸ” What’s happening here?ΒΆ

  • CryptContext manages hashing schemes (we use bcrypt)
  • hash_password() takes a raw password and returns a hashed version
  • verify_password() checks if a raw password matches a stored hash

πŸ§ͺ Example: Hashing and VerifyingΒΆ

raw = "mysecret123"
hashed = hash_password(raw)

print(hashed)
# $2b$12$... (bcrypt hash)

print(verify_password("mysecret123", hashed))  # βœ… True
print(verify_password("wrongpass", hashed))    # ❌ False

You never store or compare raw passwords. You only store the hash and use verify_password() during login.


πŸ” Where to Use This in FastAPIΒΆ

  • Signup: Hash the password before saving the user
  • Login: Verify the raw password against the stored hash

πŸ“„ routers/auth.py (signup snippet)

user.hashed_password = hash_password(user.hashed_password)
session.add(user)

πŸ“„ routers/auth.py (login snippet)

if not verify_password(form_data.password, user.hashed_password):
    raise HTTPException(status_code=401, detail="Invalid credentials")

🧠 Recap¢

Password hashing is:

  • One-way and irreversible
  • Essential for protecting user credentials
  • Best done with bcrypt via Passlib

You now have secure utilities to:

  • Hash passwords during signup
  • Verify passwords during login

πŸ§ͺ Practice ChallengeΒΆ

Create a UserCreate model with a password field.

In your signup endpoint:

  • Accept UserCreate
  • Hash the password
  • Store it as hashed_password in the database