PHP パスワードのハッシュはpassword_hash()を使おう!

  • ---

    LINEで送る
  • -

    ブックマーク
  • -

    pocket
  • -

    rss
イラストACの画像をもとに加工しています。

文字列のハッシュ化は、md5(), sha1()を使うのをやめてpassword_hash()を使いましょうという話です。

公式ドキュメントもそう言っているのに気づきました。

(遅いけど。)

ハッシュって何?

PHPの文字列のハッシュ化は、パスワードなど見られるとマズい文字列をデータベースに格納するのに使います。またキャッシュ(クッキーも)など、ファイル名から何をしているのか悟られたくないものに使います。

ハッシュは、複雑なルール(アルゴリズム)で意味不明の別の文字列に変換します。

データベースにパスワードをそのまま登録するのはセキュリティ上よくありません。

(データベースを参照されるともろバレするため。)

ハッシュ化された文字列は元に戻すことはできません。パスワードをDBに登録する処理を書いた人でさえ。

ハッシュ化する関数やクラスなどはあるが復元するものはない。

鍵師のような人が世の中にはいます。それぐらいのレベルじゃないと復元できません。

復元できないパスワードをもつ意味があるのか?

エンジニア
復元できないパスワードをDBに登録する意味あるの?

と思うかもしれませんが、ハッシュ化されたパスワードは、復元できなくても認証はできます。処理はこんな感じ。

PHPのハッシュ化処理
/**
 * パスワード登録
 */
function register_password( $id, $password ) {
    $hash = md5( $password );
    register_password_db( $id, $hash ); // 勝手に作った仮の関数
}
/**
 * パスワード取得
 */
function get_password( $id ) {
    return get_password_db( $id ); // 勝手に作った仮の関数
}

/**
 * パスワード認証
 */
function auth_password( $id, $password ) {
    $password_hash = get_password( $id );
    if ( md5( $password ) === $password_hash ) {
        return true;
    } else {
        return false;
    }
}

$id = '111';
$password = 'testpass';

// パスワード登録
register_password( $id, $password );

// パスワード認証
if ( auth_password( $id, $password ) ) {
    echo 'パスワード認証 OK';
} else {
    echo 'パスワードが間違っています。';
}

元データからハッシュ値が作れるので

ハッシュ値で比較すればいいじゃん。

ということです。これまでは。

md5(), sha1()には強い警告が出ている

PHPのハッシュ化でよく使われるのが

md5()

sha1()

です。いまでもググればこれらの関数をよく見ますが、その情報は古いです。

公式ドキュメントでも警告しています。理由は、いまのコンピュータの性能では、いともかんたんに、高速にハッシュ値から復元するからです。

PHP公式ドキュメント

md5

sha1

ネックは『常に同じハッシュ値』を作ること。

md5()やsha1()は、元データが同じならハッシュ値もつねに同じです。

$password = 'testpass';

echo md5($password) . PHP_EOL;
echo md5($password) . PHP_EOL . PHP_EOL;

echo sha1($password) . PHP_EOL;
echo sha1($password) . PHP_EOL . PHP_EOL;
結果
179ad45c6ce2cb97cf1029e212046e81
179ad45c6ce2cb97cf1029e212046e81

206c80413b9a96c1312cc346b7d2517b84463edd
206c80413b9a96c1312cc346b7d2517b84463edd

これがパスワードの復元をかんたんにします。ハッシュ値を盗まれたら、大量のパスワードのハッシュ値を作って、それを比較して当てることができるので。

同じハッシュ値を作らない関数

そこで改良したのが

password_hash()

password_hash()は同じパスワードでも、ハッシュ値はちがうものを作ります。

$password = 'testpass';
$hash = password_hash($password, PASSWORD_DEFAULT);
$hash2 = password_hash($password, PASSWORD_DEFAULT);
$hash3 = password_hash($password, PASSWORD_DEFAULT);

echo $hash . PHP_EOL;
echo $hash2 . PHP_EOL;
echo $hash3 . PHP_EOL . PHP_EOL;
結果
$2y$10$nAwvrQrv1PLLEOk3/3DvkerGDXzpB4BL15TnQQmz/BbKlSCMfhh92
$2y$10$2tqA/1/a7S9lAF.W5/NaRuGsu4kvWOINu2MLEMwDHRM4XXkP8bbaG
$2y$10$Rd.iP8CHo6y05oQkTnBSsenN1DrJB4zg2GQI1.hQL8Ng17cC/x6Qe

値がちがうだけじゃなく、作られる文字列も長くなり複雑になっています。

第2パラメータは、PASSWORD_DEFAULTを使うことを強く推奨しています。

『将来、このパラメータ自体無くしたい』ようなことも言うくらいなので。

認証も関数ひとつでかんたん

エンジニア
ちがうハッシュ値を作ったらDB値と比較できないじゃん。

と思いますが、そこはきちんと関数が用意されています。

password_verify()

$password = 'testpass';

$hash = password_hash($password, PASSWORD_DEFAULT);
$hash2 = password_hash($password, PASSWORD_DEFAULT);
$hash3 = password_hash($password, PASSWORD_DEFAULT);

echo $hash . PHP_EOL;
echo $hash2 . PHP_EOL;
echo $hash3 . PHP_EOL . PHP_EOL;

echo var_dump(password_verify($password, $hash)) ;
echo var_dump(password_verify($password, $hash2));
echo var_dump(password_verify($password, $hash3)) . PHP_EOL;

$password2 = 'testpas';
echo var_dump(password_verify($password2, $hash));
echo var_dump(password_verify($password2, $hash2));
echo var_dump(password_verify($password2, $hash3));
結果
$2y$10$hgUdeKTO0utU2KtrN2snMezeL7qdOCrNYXBNKlobIQNJj8BuAqvP.
$2y$10$VQj3q8wuWm9fj43foWdrtO.v1EXtmh6hdsWSIoyQcP2Sx3rrd7YFq
$2y$10$rwMJhUmmmXh3erFg2ADG/OOBhU76k5bPI/9OrLFkN1RJk2MVLoeBG

bool(true)
bool(true)
bool(true)

bool(false)
bool(false)
bool(false)

ちがうハッシュ値でも認証結果は同じです。

エンジニア
ハッシュ値を盗まれて、password_verify()で比較されたら大して変わらないんじゃ?

と思いますが、大事なのは

ちがうハッシュ値なのに認証できる処理の複雑さ

です。md5(), sha1()よりも複雑なアルゴリズムで処理をするので、同じような方法で復元しようとしても、解析はむずかしくなります。

(時間がかかる。)

だから、 password_hash(), password_verify()を使いましょうということですね?

password_hash()のアルゴリズムは、セキュリティ上問題があれば、より複雑にバージョンアップしていくそうです。

その他の関数

他にも、ハッシュの関数が用意されています。

password_get_info()ハッシュを作った方法の情報を返す
password_needs_rehash()ハッシュを作った方法が合っているか確認する

ハッシュ値を作ったオプションが分からない状態から認証する

ときに使います。

$password = 'testpass';

$hash = password_hash($password, PASSWORD_DEFAULT);
echo $hash . PHP_EOL . PHP_EOL;

$info = password_get_info($hash);
echo var_dump($info) . PHP_EOL . PHP_EOL;

$rehash = password_needs_rehash($hash, PASSWORD_DEFAULT, $info['options']);
echo var_dump($rehash) . PHP_EOL;
結果
$2y$10$XXpJPmmGzQ1CzhkayEz4fe7C1P51q4sMJSWNWh57D8CBeS/XW9ELO

array(3) {
  ["algo"]=>
  int(1)
  ["algoName"]=>
  string(6) "bcrypt"
  ["options"]=>
  array(1) {
    ["cost"]=>
    int(10)
  }
}


bool(false)

PHP公式ドキュメント

password_get_info

password_needs_rehash

password_***()の関数は、PHPバージョン5.5(サポート終了)以降で使えます。今は、それより前のバージョンを使うことはあまりないので気にする必要はないでしょう。

SNSでも記事を配信しています。
コメントを残す

*

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください

top
この記事を気に入ったらぜひシェアも!!