SMART LLC

crypt()関数を使ってパスワードをハッシュする方法

公開日:2014/10/12

webシステムのセキュリティ対策で必須のハッシュ処理。
万が一(だめだけど)データベースに不正アクセスされた場合、パスワードが平文だとそのまま悪用されてしまう。
PHPでパスワードを暗号化する方法をメモする。

ハッシュ関数

まずどのハッシュ関数が使えるか安全なパスワードハッシュを参考にした。
md5()関数とsha1()関数は現代では既に通用しないようなので却下。
password_hash()関数かcrypt()関数から選ぶことになりそう。
password_hash()関数を推しまくってるけど実装はバージョン5.5以降。
同等の機能のPHPライブラリもあるけど実装できるのは5.3.7以降。
ドメインキングのPHPのバージョンは5.3.3だった_(:3 」∠)_
crypt()関数を採用(´・ω・`)ショボーン

ハッシュ方式

関数が決まったのでcryptのページを見ながら作業に入る。
ここでもまだpassword_hash()関数を推してくるけど無視。
password_hash()関数もcrypt()関数のラッパーらしい。
ハッシュ方式は標準DES、拡張DES、MD5、Blowfish、SHA-256、SHA-512の6種類。
使いたい方式に合ったソルトを渡してやる必要がある。省略した場合は標準DES。
推奨されているのはBlowfishなので素直にこれを採用。
Blowfishの場合、パスワードを72文字までしか認識しないことに注意。
73文字以上は入力できないようにすればOK。

Blowfish

Blowfish方式のソルトの作り方。
'$'+プレフィックス(2桁)+'$'+コスト(2桁)+'$'+文字列(22桁)+'$'の形で生成する。
プレフィックスは2a、2x、2yの3種類。
2x、2yはバージョン5.3.7から導入ということで5.3.3の今回は2aを採用するしかない。
5.3.7以降の場合は2yを推奨してる。じゃあ2xて何だ( ゚д゚)
そもそもプレフィックスて何だて疑問は置いといてコストには04から31までの値を設定する。
値が大きい程計算コストが高く時間も負荷もかかることになる。
ここでpassword_hashのページが役に立った。
password_hash()関数でBlowfish方式を使う場合のコストのデフォルトが10だった。というわけでとりあえず10にしてみる。
あとはランダムな22桁の文字列を生成してやればよさそう。

ソルトを生成して表示してみる。

$salt = '$2a$10$';
for ($i = 0; $i < 22; $i++) { 
    $salt = $salt.substr('./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789', mt_rand(0, 63), 1); 
}
$salt = $salt.'$';
echo $salt;

こうなる。これをパスワードと一緒にcrypt()関数に渡す。

$2a$10$cbtxnzx7ZMYTJVw5WeMrqx$

crypt()関数を実行して表示してみる。

$password = 'password';
echo crypt($password, $salt);

こうなる。これをDBに格納する。

$2a$10$cbtxnzx7ZMYTJVw5WeMrqumArpdru9M9Xy6eBhE/NowXwJYz5t6mG

パスワードが正しい場合、DBのハッシュ値をソルトにcrypt()関数を実行すると…

$password = 'password';
$salt = '$2a$10$cbtxnzx7ZMYTJVw5WeMrqumArpdru9M9Xy6eBhE/NowXwJYz5t6mG';
echo crypt($password, $salt);

同じ結果が得られる。

$2a$10$cbtxnzx7ZMYTJVw5WeMrqumArpdru9M9Xy6eBhE/NowXwJYz5t6mG

パスワードが違う場合、DBのハッシュ値をソルトにcrypt()関数を実行すると…

$password = 'password1';
$salt = '$2a$10$cbtxnzx7ZMYTJVw5WeMrqumArpdru9M9Xy6eBhE/NowXwJYz5t6mG';
echo crypt($password, $salt);

同じ結果が得られない。

$2a$10$cbtxnzx7ZMYTJVw5WeMrqua6fT79KAbOZszF2g7vHC9WV.MsAljcG

これで一応パスワードの暗号化はできた。
が、世の中にはパスワードチェックの処理時間からパスワードを解析するタイミング攻撃なる脅威がある。
バージョン5.5以降ならタイミング攻撃を防ぎながらチェックしてくれるpassword_verify()関数があるのに_(:3 」∠)_
まだ調べる必要がありそうだ。

SHARE