Skip to content

Migrate password hashing from bcrypt to argon2 #28

@exhuma

Description

@exhuma

Background

Passwords are currently hashed with bcrypt (src/powonline/model.py:10, 488–498). Argon2 (specifically argon2-cffi) is the current recommended algorithm by OWASP and the Password Hashing Competition winner. It is memory-hard, which makes GPU/ASIC brute-force significantly more expensive than bcrypt.

Work required

Dependency change

  • Remove bcrypt from pyproject.toml and requirements.txt
  • Add argon2-cffi (provides argon2.PasswordHasher)

src/powonline/model.py

Replace the three bcrypt call sites with argon2.PasswordHasher:

Current Replacement
hashpw(password.encode("utf8"), gensalt()) ph.hash(password)
checkpw(password.encode("utf8"), self.password) ph.verify(self.password, password)
setpw method — same as above same

Argon2 hashes are stored as strings (str), so the password column type should be changed from BYTEA to TEXT (requires an Alembic migration).

Alembic migration

A new migration is needed to:

  1. ALTER COLUMN password TYPE TEXT USING password::text (or re-hash on first login — see note below)
  2. Optionally set a password_algorithm marker column to distinguish legacy bcrypt rows from new argon2 rows, enabling a transparent upgrade path on login (similar to the existing password_is_plaintext flag).

Transparent upgrade path (recommended)

On User.checkpw():

  1. If the stored hash starts with $2b$ (bcrypt prefix), verify with bcrypt, then re-hash and store as argon2.
  2. Otherwise verify with argon2.

This avoids a forced password reset for existing users.

Tests

Update tests/seed.sql and any fixtures that insert raw bcrypt hashes.

References

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions