If the database is leaked, an attacker gains access to every password. Hashing makes this impossible.
Why you must not store passwords in plain text
If the database is leaked, an attacker gains access to every password. Hashing makes this impossible.
passlib — the recommended library
pip install passlib[bcrypt]
from passlib.context import CryptContext
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
# Hash a password
hashed = pwd_context.hash("my_password")
# '$2b$12$...' — bcrypt hash
# Verify a password
is_valid = pwd_context.verify("my_password", hashed) # True
is_valid = pwd_context.verify("wrong_password", hashed) # False
Why bcrypt
- Intentionally slow (10–12 hashing rounds)
- Built-in salt — identical passwords produce different hashes
- Configurable
cost factor— can be increased over time - Resistant to brute-force attacks and rainbow tables
In FastAPI / SQLModel
from passlib.context import CryptContext
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
class User(SQLModel, table=True):
id: Optional[int] = Field(default=None, primary_key=True)
username: str = Field(unique=True)
hashed_password: str
def create_user(session: Session, username: str, password: str) -> User:
user = User(
username=username,
hashed_password=pwd_context.hash(password),
)
session.add(user)
session.commit()
return user
def authenticate(session: Session, username: str, password: str) -> User | None:
user = session.exec(select(User).where(User.username == username)).first()
if not user or not pwd_context.verify(password, user.hashed_password):
return None
return user
argon2 — an alternative to bcrypt
pip install passlib[argon2]
pwd_context = CryptContext(schemes=["argon2"], deprecated="auto")
Argon2 won the Password Hashing Competition in 2015 and is recommended for new projects.
💬 Comments (0)
No comments yet
Be the first to share your opinion about this article!