パスワードハッシュ(pwdlib)¶
pwdlib は passlib の後継
pwdlib は、更新が止まった passlib の代わりに使えるモダンなパスワードハッシュライブラリ。 Full Stack FastAPI Template も passlib から pwdlib に乗り換えてる。 bcrypt と Argon2 に対応してて、 passlib より設定がシンプル。 cf. pwdlib のガイド
pwdlib は
- パスワードを DB に保存するときにハッシュ化する
- ユーザーが入力したパスワードが DB 内のハッシュ化されたパスワードと同じか確かめる
ときに使える。
パッケージは uv で入れておく(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()
実行結果。
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=4(RFC 9106 の 7.4. Recommendations の SECOND RECOMMENDED option と同じ)になってる。
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 へ移行するときに便利。