Djangoでのパスワード管理
パスワード管理は、一般的に不必要に再発明されるべきではないものであり、Djangoはユーザーパスワードを管理するための安全で柔軟なツールのセットを提供するよう努めています。 このドキュメントでは、Djangoがパスワードを保存する方法、ストレージハッシュを構成する方法、およびハッシュされたパスワードを処理するためのいくつかのユーティリティについて説明します。
も参照してください
ユーザーが強力なパスワードを使用している場合でも、攻撃者は接続を盗聴できる可能性があります。 HTTPS を使用して、パスワード(またはその他の機密データ)がパスワードスニッフィングに対して脆弱になるため、プレーンHTTP接続を介してパスワードを送信しないようにします。
Djangoがパスワードを保存する方法
Djangoは柔軟なパスワードストレージシステムを提供し、デフォルトでPBKDF2を使用します。
User オブジェクトの password 属性は、次の形式の文字列です。
これらは、ユーザーのパスワードを格納するために使用されるコンポーネントであり、ドル記号で区切られ、ハッシュアルゴリズム、アルゴリズムの反復回数(作業係数)、ランダムソルト、および結果のパスワードハッシュで構成されます。 このアルゴリズムは、Djangoが使用できる多数の一方向ハッシュまたはパスワードストレージアルゴリズムの1つです。 下記参照。 反復は、アルゴリズムがハッシュに対して実行される回数を表します。 ソルトは使用されるランダムシードであり、ハッシュは一方向性関数の結果です。
デフォルトでは、Djangoは PBKDF2 アルゴリズムとSHA256ハッシュを使用します。これは、 NIST が推奨するパスワードストレッチメカニズムです。 これはほとんどのユーザーにとって十分なはずです。非常に安全であり、中断するには膨大な計算時間が必要です。
ただし、要件に応じて、別のアルゴリズムを選択したり、特定のセキュリティ状況に合わせてカスタムアルゴリズムを使用したりすることもできます。 繰り返しますが、ほとんどのユーザーはこれを行う必要はありません–よくわからない場合は、おそらくそうしないでしょう。 もしそうなら、読んでください:
Djangoは、:setting: `PASSWORD_HASHERS` 設定を参照して、使用するアルゴリズムを選択します。 これは、このDjangoインストールがサポートするハッシュアルゴリズムクラスのリストです。 このリストの最初のエントリ(つまり、settings.PASSWORD_HASHERS[0]
)はパスワードの保存に使用され、他のすべてのエントリは、既存のパスワードの確認に使用できる有効なハッシャーです。 つまり、別のアルゴリズムを使用する場合は、:setting: `PASSWORD_HASHERS` を変更して、優先するアルゴリズムをリストの最初にリストする必要があります。
:setting: `PASSWORD_HASHERS` のデフォルトは次のとおりです。
つまり、Djangoは PBKDF2 を使用してすべてのパスワードを保存しますが、PBKDF2SHA1、 argon2 、および bcrypt で保存されたパスワードのチェックをサポートします。
次のいくつかのセクションでは、上級ユーザーがこの設定を変更する一般的な方法をいくつか説明します。
DjangoでArgon2を使用する
Argon2 は、次世代のハッシュアルゴリズムを選択するためのコミュニティ主催のオープンコンペティションである2015 パスワードハッシュコンペティションの優勝者です。 通常のCPUで計算するよりも、カスタムハードウェアで計算する方が簡単ではないように設計されています。
Argon2 は、サードパーティのライブラリを必要とするため、Djangoのデフォルトではありません。 ただし、パスワードハッシュコンペティションパネルでは、Djangoでサポートされている他のアルゴリズムではなく、Argon2をすぐに使用することを推奨しています。
デフォルトのストレージアルゴリズムとしてArgon2を使用するには、次の手順を実行します。
argon2-cffiライブラリをインストールします。 これは、
python -m pip install django[argon2]
を実行することで実行できます。これは、python -m pip install argon2-cffi
と同等です(Djangoのsetup.cfg
のバージョン要件とともに)。:setting: `PASSWORD_HASHERS` を変更して、最初に
Argon2PasswordHasher
をリストします。 つまり、設定ファイルに次のように入力します。Djangoからパスワードのアップグレードが必要な場合は、このリストのエントリを保持または追加します。
Djangoでbcryptを使用する
Bcrypt は、長期的なパスワード保存用に特別に設計された、人気のあるパスワード保存アルゴリズムです。 サードパーティのライブラリを使用する必要があるため、Djangoで使用されるデフォルトではありませんが、多くの人が使用したい場合があるため、Djangoは最小限の労力でbcryptをサポートします。
デフォルトのストレージアルゴリズムとしてBcryptを使用するには、次の手順を実行します。
bcryptライブラリをインストールします。 これは、
python -m pip install django[bcrypt]
を実行することで実行できます。これは、python -m pip install bcrypt
と同等です(Djangoのsetup.cfg
のバージョン要件とともに)。:setting: `PASSWORD_HASHERS` を変更して、最初に
BCryptSHA256PasswordHasher
をリストします。 つまり、設定ファイルに次のように入力します。Djangoからパスワードのアップグレードが必要な場合は、このリストのエントリを保持または追加します。
これで、DjangoのインストールでデフォルトのストレージアルゴリズムとしてBcryptが使用されます。
塩のエントロピーを増やす
バージョン3.2の新機能。
ほとんどのパスワードハッシュには、レインボーテーブル攻撃から保護するために、パスワードハッシュとともにソルトが含まれています。 ソルト自体はランダムな値であるため、レインボーテーブルのサイズが大きくなり、コストが高くなります。現在、BasePasswordHasher
のsalt_entropy
値で128ビットに設定されています。 コンピューティングとストレージのコストが減少するにつれて、この値を上げる必要があります。 独自のパスワードハッシュを実装する場合、パスワードハッシュに必要なエントロピーレベルを使用するために、この値を自由にオーバーライドできます。 salt_entropy
はビット単位で測定されます。
実装の詳細
ソルト値の保存方法により、salt_entropy
値は事実上最小値です。 たとえば、値が128の場合、実際には131ビットのエントロピーを含むソルトが提供されます。
作業係数の増加
PBKDF2とbcrypt
PBKDF2およびbcryptアルゴリズムは、多数の反復またはハッシュのラウンドを使用します。 これにより、攻撃者の速度が意図的に低下し、ハッシュ化されたパスワードに対する攻撃が困難になります。 ただし、計算能力が向上するにつれて、反復回数を増やす必要があります。 妥当なデフォルトを選択しました(Djangoのリリースごとに増加します)が、セキュリティのニーズと利用可能な処理能力に応じて、調整することをお勧めします。 これを行うには、適切なアルゴリズムをサブクラス化し、iterations
パラメーターをオーバーライドします。 たとえば、デフォルトのPBKDF2アルゴリズムで使用される反復回数を増やすには、次のようにします。
django.contrib.auth.hashers.PBKDF2PasswordHasher
のサブクラスを作成します。これをプロジェクトのどこかに保存します。 たとえば、これを
myproject/hashers.py
のようなファイルに入れることができます。:setting: `PASSWORD_HASHERS` の最初のエントリとして新しいハッシュを追加します:
これで、DjangoインストールはPBKDF2を使用してパスワードを保存するときに、より多くの反復を使用するようになります。
Argon2
Argon2には、カスタマイズ可能な3つの属性があります。
time_cost
は、ハッシュ内の反復回数を制御します。memory_cost
は、ハッシュの計算中に使用する必要のあるメモリのサイズを制御します。parallelism
は、ハッシュの計算を並列化できるCPUの数を制御します。
これらの属性のデフォルト値はおそらくあなたにとって問題ありません。 パスワードハッシュが速すぎるか遅すぎると判断した場合は、次のように微調整できます。
parallelism
を選択して、ハッシュの計算に余裕のあるスレッドの数を指定します。memory_cost
を選択して、余裕のあるメモリのKiBにします。time_cost
を調整し、パスワードのハッシュにかかる時間を測定します。 許容できる時間のかかるtime_cost
を選択してください。time_cost
を1に設定すると、許容できないほど遅くなる場合は、memory_cost
を低くします。
memory_cost
の解釈
argon2コマンドラインユーティリティと他のいくつかのライブラリは、memory_cost
パラメータをDjangoが使用する値とは異なる方法で解釈します。 変換はmemory_cost == 2 ** memory_cost_commandline
によって与えられます。
パスワードのアップグレード
ユーザーがログインしたときに、パスワードが優先アルゴリズム以外で保存されている場合、Djangoは自動的にアルゴリズムを優先アルゴリズムにアップグレードします。 これは、ユーザーがログインすると、Djangoの古いインストールが自動的により安全になることを意味します。また、発明されたときに新しい(より優れた)ストレージアルゴリズムに切り替えることができることも意味します。
ただし、Djangoは:setting: `PASSWORD_HASHERS` に記載されているアルゴリズムを使用するパスワードのみをアップグレードできるため、新しいシステムにアップグレードするときは、このリストからエントリを削除しないようにしてください。 そうした場合、言及されていないアルゴリズムを使用しているユーザーはアップグレードできなくなります。 ハッシュされたパスワードは、PBKDF2の反復回数、bcryptラウンド、またはargon2属性を増やす(または減らす)と更新されます。
データベース内のすべてのパスワードがデフォルトのハッシャーのアルゴリズムでエンコードされていない場合、パスワードがエンコードされたユーザーのログイン要求の期間が異なるため、ユーザー列挙タイミング攻撃に対して脆弱になる可能性があることに注意してください。デフォルト以外のアルゴリズムと、存在しないユーザー(デフォルトのハッシャーを実行する)のログイン要求の期間。 古いパスワードハッシュをアップグレードすることで、これを軽減できる場合があります。
ログインを必要としないパスワードのアップグレード
MD5やSHA1などの古い弱いハッシュを持つ既存のデータベースがある場合は、ユーザーがログインしたときにアップグレードが行われるのを待つのではなく、自分でそれらのハッシュをアップグレードすることをお勧めします(ユーザーがログインしない場合は決して発生しない可能性があります)あなたのサイトに戻る)。 この場合、「ラップされた」パスワードハッシャーを使用できます。
この例では、SHA1ハッシュのコレクションを移行してPBKDF2(SHA1(password))を使用し、ユーザーがログイン時に正しいパスワードを入力したかどうかを確認するための対応するパスワードハッシュを追加します。 組み込みのUser
モデルを使用しており、プロジェクトにaccounts
アプリがあることを前提としています。 パターンを変更して、任意のアルゴリズムまたはカスタムユーザーモデルで機能するようにすることができます。
まず、カスタムハッシャーを追加します。
アカウント/hashers.py
データ移行は次のようになります。
アカウント/移行/0002_migrate_sha1_passwords.py
ハードウェアの速度にもよりますが、この移行には数千人のユーザーにとって数分程度かかることに注意してください。
最後に、:setting: `PASSWORD_HASHERS` 設定を追加します。
mysite / settings.py
サイトで使用している他のハッシャーをこのリストに含めます。
含まれているハッシャー
Djangoに含まれるハッシャーの完全なリストは次のとおりです。
対応するアルゴリズム名は次のとおりです。
pbkdf2_sha256
pbkdf2_sha1
argon2
bcrypt_sha256
bcrypt
sha1
md5
unsalted_sha1
unsalted_md5
crypt
あなた自身のハッシャーを書く
反復回数などの作業要素を含む独自のパスワードハッシャーを作成する場合は、harden_runtime(self, password, encoded)
メソッドを実装して、encoded
パスワードで提供される作業要素とハッシャーのデフォルトの作業係数。 これにより、古い反復回数でエンコードされたパスワードを持つユーザーのログイン要求と、存在しないユーザー(デフォルトのハッシャーのデフォルトの反復回数を実行する)との違いによるユーザー列挙タイミング攻撃が防止されます。
PBKDF2を例にとると、encoded
に20,000回の反復が含まれ、ハッシャーのデフォルトのiterations
が30,000の場合、メソッドはpassword
をPBKDF2のさらに10,000回の反復で実行する必要があります。
ハッシャーに作業要素がない場合は、メソッドをno-op(pass
)として実装します。
ユーザーのパスワードを手動で管理する
django.contrib.auth.hashers モジュールは、ハッシュ化されたパスワードを作成および検証するための一連の関数を提供します。 User
モデルとは別に使用できます。
- check_password(password, encoded)
- プレーンテキストのパスワードをデータベース内のハッシュされたパスワードと比較してユーザーを手動で認証する場合は、便利な関数 check_password()を使用します。 チェックするプレーンテキストのパスワードと、チェックするデータベース内のユーザーの
password
フィールドの完全な値の、2つの引数を取り、一致する場合はTrue
を返します。 [ X180X]それ以外の場合。
- make_password(password, salt=None, hasher='default')
このアプリケーションで使用される形式でハッシュ化されたパスワードを作成します。 必須の引数が1つあります。プレーンテキスト(文字列またはバイト)のパスワードです。 オプションで、デフォルト(
PASSWORD_HASHERS
設定の最初のエントリ)を使用したくない場合は、使用するソルトとハッシュアルゴリズムを提供できます。 各ハッシャーのアルゴリズム名については、付属のハッシャーを参照してください。 パスワード引数がNone
の場合、使用できないパスワードが返されます( check_password()によって受け入れられることはありません)。バージョン3.1で変更:
password
パラメーターは、None
でない場合は、文字列またはバイトである必要があります。
- is_password_usable(encoded_password)
- パスワードが User.set_unusable_password()の結果である場合、
False
を返します。
パスワードの検証
ユーザーはしばしば不適切なパスワードを選択します。 この問題を軽減するために、Djangoはプラグイン可能なパスワード検証を提供しています。 複数のパスワードバリデーターを同時に構成できます。 Djangoにはいくつかのバリデーターが含まれていますが、独自のバリデーターを作成することもできます。
各パスワードバリデーターは、ユーザーに要件を説明するためのヘルプテキストを提供し、指定されたパスワードを検証し、要件を満たしていない場合はエラーメッセージを返し、オプションで設定されたパスワードを受け取る必要があります。 バリデーターには、動作を微調整するためのオプション設定を設定することもできます。
検証は、:setting: `AUTH_PASSWORD_VALIDATORS` 設定によって制御されます。 設定のデフォルトは空のリストです。これは、バリデーターが適用されないことを意味します。 デフォルトの:djadmin: `startproject` テンプレートで作成された新しいプロジェクトでは、バリデーターのセットがデフォルトで有効になっています。
デフォルトでは、バリデーターは、パスワードをリセットまたは変更するためのフォームと、:djadmin: `createsuperuser` および:djadmin:` changepassword` 管理コマンドで使用されます。 バリデーターはモデルレベルでは適用されません。たとえば、User.objects.create_user()
やcreate_superuser()
では、ユーザーではなく開発者がそのレベルでDjangoとやり取りすることを前提としているため、またモデルの検証では適用されないためです。モデル作成の一部として自動的に実行されます。
ノート
パスワードの検証により、多くの種類の脆弱なパスワードの使用を防ぐことができます。 ただし、パスワードがすべてのバリデーターを通過するという事実は、それが強力なパスワードであることを保証するものではありません。 最先端のパスワードバリデーターでも検出できない、パスワードを弱める可能性のある多くの要因があります。
パスワード検証の有効化
パスワードの検証は、:setting: `AUTH_PASSWORD_VALIDATORS` 設定で構成されます:
この例では、含まれている4つのバリデーターすべてを有効にします。
UserAttributeSimilarityValidator
は、パスワードとユーザーの属性セットとの類似性をチェックします。MinimumLengthValidator
。パスワードが最小の長さを満たしているかどうかを確認します。 このバリデーターはカスタムオプションで構成されています。最小長はデフォルトの8文字ではなく9文字である必要があります。CommonPasswordValidator
。一般的なパスワードのリストにパスワードが含まれているかどうかを確認します。 デフォルトでは、含まれている20,000個の一般的なパスワードのリストと比較されます。NumericPasswordValidator
、パスワードが完全に数値ではないかどうかをチェックします。
UserAttributeSimilarityValidator
およびCommonPasswordValidator
の場合、この例ではデフォルト設定を使用しています。 NumericPasswordValidator
には設定がありません。
ヘルプテキストとパスワードバリデーターからのエラーは、常に:setting: `AUTH_PASSWORD_VALIDATORS` にリストされている順序で返されます。
含まれているバリデーター
Djangoには4つのバリデーターが含まれています。
- class MinimumLengthValidator(min_length=8)
- パスワードが最小の長さを満たしているかどうかを検証します。 最小の長さは、
min_length
パラメーターでカスタマイズできます。
- class UserAttributeSimilarityValidator(user_attributes=DEFAULT_USER_ATTRIBUTES, max_similarity=0.7)
パスワードがユーザーの特定の属性と十分に異なるかどうかを検証します。
user_attributes
パラメーターは、比較するユーザー属性の名前を反復可能にする必要があります。 この引数が指定されていない場合、デフォルトが使用されます:'username', 'first_name', 'last_name', 'email'
。 存在しない属性は無視されます。拒否されたパスワードの最小類似性は、
max_similarity
パラメーターを使用して0から1のスケールで設定できます。 0に設定すると、すべてのパスワードが拒否されますが、1に設定すると、属性の値と同じパスワードのみが拒否されます。
- class CommonPasswordValidator(password_list_path=DEFAULT_PASSWORD_LIST_PATH)
パスワードが一般的なパスワードではないかどうかを検証します。 これにより、パスワードが小文字に変換され(大文字と小文字を区別しない比較が行われます)、 Royce Williams によって作成された20,000個の一般的なパスワードのリストと照合されます。
password_list_path
は、共通パスワードのカスタムファイルのパスに設定できます。 このファイルには、1行に1つの小文字のパスワードが含まれている必要があり、プレーンテキストまたはgzipで圧縮されている場合があります。
- class NumericPasswordValidator
- パスワードが完全に数値ではないかどうかを検証します。
検証の統合
django.contrib.auth.password_validation
には、パスワード検証を統合するために独自のフォームまたは他のコードから呼び出すことができるいくつかの関数があります。 これは、パスワード設定にカスタムフォームを使用する場合や、パスワードの設定を許可するAPI呼び出しがある場合などに役立ちます。
- validate_password(password, user=None, password_validators=None)
パスワードを検証します。 すべてのバリデーターがパスワードが有効であると判断した場合、
None
を返します。 1つ以上のバリデーターがパスワードを拒否した場合、バリデーターからのすべてのエラーメッセージとともに ValidationError を発生させます。user
オブジェクトはオプションです。指定されていない場合、一部のバリデーターは検証を実行できず、パスワードを受け入れる可能性があります。
- password_changed(password, user=None, password_validators=None)
パスワードが変更されたことをすべてのバリデーターに通知します。 これは、パスワードの再利用を防ぐバリデーターなどで使用できます。 パスワードが正常に変更されたら、これを呼び出す必要があります。
AbstractBaseUser のサブクラスの場合、 set_password()を呼び出すと、パスワードフィールドは「ダーティ」としてマークされ、ユーザーが保存された後に
password_changed()
への呼び出しがトリガーされます。
- password_validators_help_texts(password_validators=None)
- すべてのバリデーターのヘルプテキストのリストを返します。 これらは、ユーザーにパスワード要件を説明します。
- password_validators_help_text_html(password_validators=None)
<ul>
内のすべてのヘルプテキストを含むHTML文字列を返します。 これは、フォームフィールドのhelp_text
パラメーターに出力を直接渡すことができるため、フォームにパスワード検証を追加するときに役立ちます。
- get_password_validators(validator_config)
validator_config
パラメーターに基づいてバリデーターオブジェクトのセットを返します。 デフォルトでは、すべての関数は:setting: `AUTH_PASSWORD_VALIDATORS` で定義されたバリデーターを使用しますが、この関数を代替のバリデーターセットで呼び出し、結果をpassword_validators
パラメーターに渡します。他の関数では、代わりにバリデーターのカスタムセットが使用されます。 これは、ほとんどのシナリオで使用する典型的なバリデーターのセットがあるが、カスタムセットを必要とする特別な状況がある場合にも役立ちます。 常に同じバリデーターのセットを使用する場合は、:setting: `AUTH_PASSWORD_VALIDATORS` の構成がデフォルトで使用されるため、この関数を使用する必要はありません。validator_config
の構造は、:setting: `AUTH_PASSWORD_VALIDATORS` の構造と同じです。 この関数の戻り値は、上記の関数のpassword_validators
パラメーターに渡すことができます。
パスワードがこれらの関数の1つに渡される場合、これは常にクリアテキストのパスワードである必要があります。ハッシュされたパスワードではありません。
独自のバリデーターを書く
Djangoの組み込みバリデーターでは不十分な場合は、独自のパスワードバリデーターを作成できます。 バリデーターのインターフェースはかなり小さいです。 2つのメソッドを実装する必要があります。
validate(self, password, user=None)
:パスワードを検証します。 パスワードが有効な場合はNone
を返し、パスワードが無効な場合はエラーメッセージとともに ValidationError を発生させます。user
がNone
であることに対処できる必要があります。それがバリデーターを実行できないことを意味する場合は、エラーなしでNone
を返します。get_help_text()
:要件をユーザーに説明するためのヘルプテキストを提供します。
バリデーターの:setting: `AUTH_PASSWORD_VALIDATORS` のOPTIONS
内のすべてのアイテムは、コンストラクターに渡されます。 すべてのコンストラクター引数にはデフォルト値が必要です。
オプションの設定が1つある、バリデーターの基本的な例を次に示します。
password_changed(password, user=None
)を実装することもできます。これは、パスワードが正常に変更された後に呼び出されます。 これは、たとえば、パスワードの再利用を防ぐために使用できます。 ただし、ユーザーの以前のパスワードを保存する場合は、クリアテキストで保存しないでください。