I am trying to compare two passwords using bcrypt. The hashed password is stored in the database as a string. When I compare the two hashed passwords (the one from the database and the one from the user), I make sure they are both encoded, but the passwords still do not match.
Why do the hashed passwords not match when I store it in a database? Is it because the salt is different than when the hash password was originally generated? If so, how am I supposed to check that a password matches what is stored in a database?
code:
import os
import bcrypt
from sqlalchemy import Column, Integer, String, create_engine
from sqlalchemy.orm import declarative_base, sessionmaker
Base = declarative_base()
os.remove("users.db") if os.path.exists("users.db") else None
engine = create_engine("sqlite:///users.db")
Session = sessionmaker(bind=engine)
session = Session()
class User(Base):
__tablename__ = "users"
id = Column(Integer, primary_key=True)
username = Column(String(50), unique=True, nullable=False)
password_hash = Column(String(100), nullable=False)
Base.metadata.create_all(engine)
def hash_password(password: str) -> str:
hashed = bcrypt.hashpw(password.encode("utf-8"), bcrypt.gensalt())
return hashed.decode("utf-8")
def create_user(username: str, password: str) -> None:
hashed_password = hash_password(password)
user = User(username=username, password_hash=hashed_password)
session.add(user)
session.commit()
print(f"User '{username}' created.")
print(f'password: {password}')
print(f'password_hash: {hashed_password}')
print()
def verify_password(username: str, password_to_check: str) -> bool:
user = session.query(User).filter_by(username=username).first()
if not user:
print("User not found.")
return False
match = bcrypt.checkpw(
hash_password(password_to_check).encode('utf-8'), user.password_hash.encode("utf-8")
)
if match:
print("Password match!")
else:
print("Password does not match.")
print('password_to_check:', password_to_check)
print('password_to_check (encode):', hash_password(password_to_check).encode('utf-8'))
print('user.password_hash:', user.password_hash)
print('user.password_hash (encode):', user.password_hash.encode('utf-8'))
return match
# Create a user
create_user("alice", "my_secure_password")
# Try to verify password
verify_password("alice", "my_secure_password") # Should match
print()
verify_password("alice", "wrong_password") # Should not match
print()
output:
User 'alice' created.
password: my_secure_password
password_hash: $2b$12$Q3NmS7nQ.hvg7NnUk8bONO3.tVHmB0IC758BBKXXzT2omFuBp4xd2
Password does not match.
password_to_check: my_secure_password
password_to_check (encode): b'$2b$12$veGsFll.mjNjK10GXYYLqe9at/9FN.2Ue12T.l9BedjbHIQmEVWDe'
user.password_hash: $2b$12$Q3NmS7nQ.hvg7NnUk8bONO3.tVHmB0IC758BBKXXzT2omFuBp4xd2
user.password_hash (encode): b'$2b$12$Q3NmS7nQ.hvg7NnUk8bONO3.tVHmB0IC758BBKXXzT2omFuBp4xd2'
Password does not match.
password_to_check: wrong_password
password_to_check (encode): b'$2b$12$x6CdcXKwfIjgwcAWiAkU8.1ukRrKUVmgmBFY5Sw438odzK6X7D4fG'
user.password_hash: $2b$12$Q3NmS7nQ.hvg7NnUk8bONO3.tVHmB0IC758BBKXXzT2omFuBp4xd2
user.password_hash (encode): b'$2b$12$Q3NmS7nQ.hvg7NnUk8bONO3.tVHmB0IC758BBKXXzT2omFuBp4xd2'
Even in this most basic example, they still do not match:
match = bcrypt.checkpw(
hash_password('my_secure_password').encode('utf-8'),
hash_password('my_secure_password').encode("utf-8"))
> False