PHP
パスワードハッシュ関数
サーチ…
前書き
より安全なWebサービスがパスワードをプレーンテキスト形式で保存するのを避けるため、PHPなどの言語は、より安全な業界標準をサポートするためのさまざまな(解読不能な)ハッシュ関数を提供します。このトピックでは、PHPで適切なハッシングを行うためのドキュメントを提供します。
構文
-
string password_hash ( string $password , integer $algo [, array $options ] )
-
boolean password_verify ( string $password , string $hash )
-
boolean password_needs_rehash ( string $hash , integer $algo [, array $options ] )
-
array password_get_info ( string $hash )
備考
PHP 5.5より前のバージョンでは、互換性パックを使用してpassword_*
関数を提供することができpassword_*
。できるだけ互換性パックを使用することを強くお勧めします。
互換性パックの有無にかかわらず、 crypt()
による正しいBcrypt機能はPHP 5.3.7以降に依存します。そうしないと、パスワードをASCII文字のみの文字セットに制限する必要があります。
注: PHP 5.5以降を使用している場合、 サポートされていないバージョンのPHPを使用しています。これは、セキュリティアップデートをもう受信しません。できるだけ早く更新してください。その後、パスワードハッシュを更新することができます。
アルゴリズムの選択
安全なアルゴリズム
- ブルートフォース攻撃が極端に遅くなるため、ハッシュ計算時間を長くするために鍵ストレッチを使用する限り、 bcryptは最良のオプションです。
- argon2はPHP 7.2で利用可能な別のオプションです。
安全でないアルゴリズム
以下のハッシュアルゴリズムは、目的に 応じて安全でないか、または不適当であるため 、 使用しないでください 。それらは、パスワードハッシュには適していませんでした。なぜなら、パスワードのハッシュを遅くて難しくするのではなく、速いダイジェスト用に設計されていたからです。
いずれかを使用する場合は 、塩類も含めて 、 できるだけ早く推奨される安全なアルゴリズムに切り替える必要があります。
安全でないと考えられるアルゴリズム:
- MD4 - 1995年に発見された衝突攻撃
- MD5 - 2005年に発見された衝突攻撃
- SHA-1 - 2015年に実証された衝突攻撃
いくつかのアルゴリズムは、信頼性を証明するメッセージダイジェストアルゴリズムとして安全に使用できますが、 決してパスワードハッシングアルゴリズムとしては使用できません 。
- SHA-2
- SHA-3
SHA256やSHA512などの強力なハッシュは途切れにくく、堅牢ですが、 bcryptやargon2ハッシュ関数を使用する方が一般的に安全です。これらのアルゴリズムに対するブルートフォース攻撃は従来のコンピュータでは非常に困難です。
既存のパスワードハッシュをより強力なアルゴリズムにアップグレードできるかどうかを判断する
PASSWORD_DEFAULT
メソッドを使用してシステムにパスワードをハッシュする最適なアルゴリズムを選択させる場合、強度がデフォルトで増加するため、ユーザーのログイン時に古いパスワードを再ハッシュしたい場合があります
<?php
// first determine if a supplied password is valid
if (password_verify($plaintextPassword, $hashedPassword)) {
// now determine if the existing hash was created with an algorithm that is
// no longer the default
if (password_needs_rehash($hashedPassword, PASSWORD_DEFAULT)) {
// create a new hash with the new default
$newHashedPassword = password_hash($plaintextPassword, PASSWORD_DEFAULT);
// and then save it to your data store
//$db->update(...);
}
}
?>
システムでpassword_ *関数が利用できない場合(以下の注釈にリンクされている互換性パックを使用することはできません)、アルゴリズムを決定して、次のような方法で元のハッシュを作成することができます。
<?php
if (substr($hashedPassword, 0, 4) == '$2y$' && strlen($hashedPassword) == 60) {
echo 'Algorithm is Bcrypt';
// the "cost" determines how strong this version of Bcrypt is
preg_match('/\$2y\$(\d+)\$/', $hashedPassword, $matches);
$cost = $matches[1];
echo 'Bcrypt cost is '.$cost;
}
?>
パスワードハッシュの作成
password_hash()
を使用して、現在の業界ベストプラクティスの標準ハッシュまたはキー派生を使用して、パスワードハッシュを作成します。書面では、標準はbcryptです。つまり、 PASSWORD_DEFAULT
にはPASSWORD_DEFAULT
と同じ値が含まれPASSWORD_BCRYPT
ます。
$options = [
'cost' => 12,
];
$hashedPassword = password_hash($plaintextPassword, PASSWORD_DEFAULT, $options);
3番目のパラメータは必須ではありません 。
'cost'
値は、運用サーバーのハードウェアに基づいて選択する必要があります。パスワードを増やすと、パスワードを生成するコストが高くなります。高価な人はそれを生成するためにそれを分解しようとする誰もがかかるように長い時間を生成することです。理想的には、コストは可能な限り高くする必要がありますが、実際にはすべてを遅くしないように設定する必要があります。 0.1秒から0.4秒の間のどこかに問題はありません。疑義がある場合は、デフォルト値を使用してください。
5.5.0より小さいPHPでは、 password_*
関数は利用できません。これらの機能を置き換えるには、互換性パックを使用する必要があります。互換性パックには、PHP 5.3.7以降、または$2y
修正がバックポートされたバージョン(RedHatなど)が必要です。
それらを使用できない場合は、 crypt()
パスワードハッシュを実装できます。password_hash password_hash()
はcrypt()
関数のラッパーとして実装されているため、機能を失う必要はありません。
// this is a simple implementation of a bcrypt hash otherwise compatible
// with `password_hash()`
// not guaranteed to maintain the same cryptographic strength of the full `password_hash()`
// implementation
// if `CRYPT_BLOWFISH` is 1, that means bcrypt (which uses blowfish) is available
// on your system
if (CRYPT_BLOWFISH == 1) {
$salt = mcrypt_create_iv(16, MCRYPT_DEV_URANDOM);
$salt = base64_encode($salt);
// crypt uses a modified base64 variant
$source = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
$dest = './ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
$salt = strtr(rtrim($salt, '='), $source, $dest);
$salt = substr($salt, 0, 22);
// `crypt()` determines which hashing algorithm to use by the form of the salt string
// that is passed in
$hashedPassword = crypt($plaintextPassword, '$2y$10$'.$salt.'$');
}
パスワードハッシュ用の塩
暗号アルゴリズムの信頼性にもかかわらず、まだレインボーテーブルに対する脆弱性が存在します。それが塩を使うことが推奨される理由です。
saltとは、ハッシングの前にパスワードに付加されてソース文字列を一意にするものです。 2つの同一のパスワードが与えられた場合、結果として得られるハッシュは、その塩が一意であるためユニークなものになります。
ランダムな塩は、パスワードセキュリティの最も重要な部分の1つです。これは、既知のパスワードハッシュのルックアップテーブルであっても、ランダムな塩が使用されているため、攻撃者はデータベースのパスワードハッシュとユーザーのパスワードハッシュを一致させることができないことを意味します。あなたはいつも無作為かつ暗号的に安全な塩を使用するべきです。 続きを読む
password_hash()
bcrypt
アルゴリズムを使用すると、結果のハッシュと一緒にプレーンテキストソルトが保存されます。つまり、ハッシュを異なるシステムやプラットフォームで転送して元のパスワードと照合することができます。
これが推奨されていない場合でも、 salt
オプションを使用して独自のランダムな塩を定義することができます。
$options = [
'salt' => $salt, //see example below
];
重要です。このオプションを省略すると、パスワードハッシュごとにpassword_hash()によってランダムなsaltが生成されます。これが意図された動作モードです。
saltオプションは、PHP 7.0.0以降で廃止されました。既定で生成される塩を単に使用することが今や好ましい。
ハッシュに対してパスワードを確認する
password_verify()
は、既知のハッシュに対するパスワードの有効性を検証するために提供される組み込み関数password_verify()
PHP 5.5以降)。
<?php
if (password_verify($plaintextPassword, $hashedPassword)) {
echo 'Valid Password';
}
else {
echo 'Invalid Password.';
}
?>
サポートされているすべてのハッシュアルゴリズムは、ハッシュ自体で使用されたハッシュを識別する情報を格納するため、平文パスワードをエンコードするために使用するアルゴリズムを指定する必要はありません。
あなたのシステムでpassword_ *関数が利用できない場合(以下の注釈にリンクされている互換性パックを使用することはできません)、 crypt()
関数を使ってパスワード検証を実装することができます。 タイミング攻撃を避けるために、特定の予防策を講じなければならないことに注意してください。
<?php
// not guaranteed to maintain the same cryptographic strength of the full `password_hash()`
// implementation
if (CRYPT_BLOWFISH == 1) {
// `crypt()` discards all characters beyond the salt length, so we can pass in
// the full hashed password
$hashedCheck = crypt($plaintextPassword, $hashedPassword);
// this a basic constant-time comparison based on the full implementation used
// in `password_hash()`
$status = 0;
for ($i=0; $i<strlen($hashedCheck); $i++) {
$status |= (ord($hashedCheck[$i]) ^ ord($hashedPassword[$i]));
}
if ($status === 0) {
echo 'Valid Password';
}
else {
echo 'Invalid Password';
}
}
?>