Skip to content

パスワードハッシュ(pwdlib)

pwdlib は passlib の後継

pwdlib は、更新が止まった passlib の代わりに使えるモダンなパスワードハッシュライブラリ。 Full Stack FastAPI Template も passlib から pwdlib に乗り換えてる。 bcrypt と Argon2 に対応してて、 passlib より設定がシンプル。 cf. pwdlib のガイド

pwdlib は

  • パスワードを DB に保存するときにハッシュ化する
  • ユーザーが入力したパスワードが DB 内のハッシュ化されたパスワードと同じか確かめる

ときに使える。

パッケージは uv で入れておく(bcrypt バックエンドも一緒に)。

uv add "pwdlib[bcrypt]"


コード例(bcrypt)

  • Alice のパスワード(alice_pw = "kokokoko")をハッシュ化した値を表示
  • 正しい平文パスワードとハッシュ化したパスワードで一致するかを表示
  • 間違った平文パスワードとハッシュ化したパスワードで一致するかを表示
# pwdlib==0.3.0
from pwdlib import PasswordHash
from pwdlib.hashers.bcrypt import BcryptHasher

password_hash = PasswordHash([BcryptHasher()])

alice_pw = "kokokoko"
wrong_alice_pw = "ooookkkk"

def get_password_hash(password: str) -> str:
    return password_hash.hash(password)

def verify_password(plain_password: str, hashed_password: str) -> bool:
    return password_hash.verify(plain_password, hashed_password)

def main():
    hashed_alice_pw = get_password_hash(alice_pw)
    print(hashed_alice_pw)
    pw_match = verify_password(alice_pw, hashed_alice_pw)
    pw_mismatch = verify_password(wrong_alice_pw, hashed_alice_pw)
    print(pw_match)
    print(pw_mismatch)

if __name__ == "__main__":
    main()

実行結果。

% uv run python main.py
$2b$12$rA/tsIiaegsKKOQdYo42AOaIDb0q7AZ3BfUuIkMZlhadn5W/aLQFa
True
False

passlib の CryptContext と違って、 pwdlib は PasswordHash に使いたいハッシャー(ここでは BcryptHasher)のリストを渡す形: cf. pwdlib リファレンス


Argon2id

OWASP Password Storage Cheat Sheet では、まずは Argon2id の使用を検討することが書かれてる。

pwdlib は Argon2 用のハッシャー(Argon2Hasher)を用意してて、 passlib のように argon2_cffi を別途入れてパラメータを渡す手間はない。 Argon2Hasher()デフォルトがそのまま Argon2id で、パラメータも m=65536, t=3, p=4RFC 9106 の 7.4. Recommendations の SECOND RECOMMENDED option と同じ)になってる。

argon2 バックエンドを入れておく。

uv add "pwdlib[argon2]"


コード例(Argon2id)

  • Alice のパスワード(alice_pw = "kokokoko")を Argon2id でハッシュ化した値を表示
  • 正しい平文パスワードとハッシュ化したパスワードで一致するかを表示
  • 間違った平文パスワードとハッシュ化したパスワードで一致するかを表示
# pwdlib==0.3.0
from pwdlib import PasswordHash
from pwdlib.hashers.argon2 import Argon2Hasher

password_hash = PasswordHash([Argon2Hasher()])

alice_pw = "kokokoko"
wrong_alice_pw = "ooookkkk"

def get_password_hash(password: str) -> str:
    return password_hash.hash(password)

def verify_password(plain_password: str, hashed_password: str) -> bool:
    return password_hash.verify(plain_password, hashed_password)

def main():
    hashed_alice_pw = get_password_hash(alice_pw)
    print(hashed_alice_pw)
    pw_match = verify_password(alice_pw, hashed_alice_pw)
    pw_mismatch = verify_password(wrong_alice_pw, hashed_alice_pw)
    print(pw_match)
    print(pw_mismatch)

if __name__ == "__main__":
    main()

実行結果。

% uv run python main.py
$argon2id$v=19$m=65536,t=3,p=4$eW6z0jrS4e8mTsL1KYR0yQ$nWzmJZSzKEgfOuavBBRagGlRf1zlE2gLQqc18MgPuxo
True
False

ハッシュ化されたパスワードの先頭に $argon2id$v=19$m=65536,t=3,p=4$ とあって、 passlib のときと同じく RFC 9106 の SECOND RECOMMENDED option のとおりに設定できてる(pwdlib はこれがデフォルト)。

  • argon2id → Argon2id を使えてる
  • m=65536 → メモリコストは 65536 KiB = 64MiB になってる
  • t=3 → 繰り返し回数は 3 回になってる

ちなみに PasswordHash.recommended() を使うと、 Argon2 を優先しつつ bcrypt のハッシュも検証できるハッシャーになるので、 bcrypt から Argon2 へ移行するときに便利。

password_hash = PasswordHash.recommended()