⌨️

passlibのCryptContextでbcryptを使用して生成したハッシュ値のソルトについて

2023/02/19に公開

はじめに

みなさんFastAPIは使っていますか?

FastAPI は、標準の Python 型ヒントに基づいて Python で API を構築するための最新の高速 (高性能) Web フレームワークです。
主な機能は次のとおりです。
高速: NodeJSやGoと同等の非常に高いパフォーマンス(Starlette と Pydantic のおかげです)。利用可能な最も高速な Python フレームワークの 1 つです。
高速コーディング: 機能の開発速度が約 200% ~ 300% 向上します。*
バグの減少: 人的 (開発者) によるエラーを約 40% 削減します。*
直感的: 優れたエディター サポート。どこでも補完。デバッグにかかる​​時間が短縮されます。
簡単: 使いやすく、学習しやすいように設計されています。ドキュメントを読む時間が短縮されます。
要約: コードの重複を最小限に抑えます。各パラメータ宣言から複数の機能が得られます。バグが少なくなります。
堅牢: 自動インタラクティブ ドキュメントを使用して、すぐに実稼働可能なコードを取得します。
標準ベース: API のオープン スタンダードに基づいており、完全に互換性があります。オープンAPI(旧称Swagger)とJSONスキーマ。

中でも私は非同期処理、型付け、JSONスキーマの自動生成が気に入っており、かつPython自体の機械学習との相性が良いこともあって愛用しています。基本的に公式docsはとても分かりやすいのですが、ハンズオンの際にpasslibによって生成されたハッシュにソルトが含まれているのかどうか、そしてどのように管理すれば良いのかが分からなかったので、補足として記事を書くことにしました。

まずはpasslibをinstall

pip install passlib[bcrypt]

passlib.CryptContextを使ってハッシュを生成・検証する

from passlib.context import CryptContext

pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")

password = 'secret'
hashed = pwd_context.hash(password)

print(hashed)
# $2b$12$BkQYJsyCu9Dc/.iRv/P.XeRF6wmVmp0PMFVB4QSzsLCwI0cm.dyje
print(pwd_context.verify(password, hashed))
# True

ここで生成されたハッシュにはソルトが含まれています(hashed[7:29])。そのため、ソルトを含んだ状態でハッシュをデータベースに保存してしまえば、管理や検証に関して気にする必要はありません。

print(f'salt: {hashed[7:29]}')
# salt: BkQYJsyCu9Dc/.iRv/P.Xe
print(f'hash: {hashed[29:]}')
# hash: RF6wmVmp0PMFVB4QSzsLCwI0cm.dyje

このソルトはpwd_context.hashを実行するたびにランダムに生成されるため、ハッシュの値も毎回変わります。CryptContextは、検証対象のハッシュからソルトを取り出して計算しているようです。

手動でソルトを取り出しハッシュを検証する

passlib.hash.bcrypt.hashでは、ソルトを指定することができます。

import passlib.hash

def my_verify(password, hashed):

    salt = hashed[7:29]
    _hashed = passlib.hash.bcrypt.hash(password, salt=salt)

    return hashed == _hashed

print(my_verify(password, hashed))
# True

Discussion