PHP の crypt で Blowfish を使う

PHPでパスワードをDBとかに格納する際にハッシュ化するのは常識として、その場合のハッシュ化をどうするか

5.5以上ではpassword_hashという関数があるので、それを使えばいいとして、それ以前のバージョンでの場合

PHP: crypt - Manual
PHP: パスワードのハッシュ - Manual

マニュアルを見てわかりにくかったので自分用メモ

CRYPT_BLOWFISH - Blowfish ハッシュ。salt の形式は、 "$2a$" か "$2x$" あるいは "$2y$"、2 桁のコストパラメータ、"$"、そして文字 "./0-9A-Za-z" からなる 22 文字となります。 この範囲外の文字を salt に使うと、crypt() は長さゼロの文字列を返します。 2 桁のコストパラメータは反復回数の 2 を底とする対数で、 これは Blowfish ベースのハッシュアルゴリズムで使います。 この値は 04 から 31 までの範囲でなければならず、 それ以外の値の場合は crypt() は失敗します。 5.3.7 までのバージョンの PHP では、salt のプレフィックスとして "$2a$" だけしか使えませんでした。PHP 5.3.7 で新たなプレフィックスが導入され、 Blowfish の実装にあったセキュリティ上の弱点に対応しました。 セキュリティ修正の対応の詳細については ≫ この文書 を参照ください。 簡単にまとめると、PHP 5.3.7 以降しか使わないのなら "$2a$" ではなく "$2y$" を使うべきだということです。

crypt関数のサンプルを見ると下記のような感じになっている

<?php
$hashed_password = crypt('mypassword'); // saltを自動的に生成させます

/* 異なったハッシュアルゴリズムが使用された際の問題を避けるために
   crypt()の結果全体をパスワード比較用のsaltとして渡す必要があります。
   (上記のように標準DESに基づくパスワードハッシュは2文字のsaltを使用します
   が、MD5に基づくハッシュは12文字のsaltを使用します) */
if (crypt($user_input, $hashed_password) == $hashed_password) {
   echo "Password verified!";
}
?>

Blowfish用に若干書き換え

<?php

$password = 'mypassword';

$hashed_password = crypt($password, '$2y$04$abcdefghijklmnopqrstuv');

$user_input = 'mypassword';

if(crypt($user_input, $hashed_password)==$hashed_password){
	echo 'Password verified!' . PHP_EOL;
}

var_dump(
	$hashed_password,
	crypt($user_input, $hashed_password)
);

上記の実行結果

Password verified!
string(60) "$2y$04$abcdefghijklmnopqrstuu9oZQP0E7UMk4NbKn6RLt8mFl9OFuldW"	# $hashed_passwordの中身
string(60) "$2y$04$abcdefghijklmnopqrstuu9oZQP0E7UMk4NbKn6RLt8mFl9OFuldW"	# crypt($user_input, $hashed_password) の中身

crypt関数の第2引数にハッシュ化した後の値を渡して、それとハッシュ化したパスワードを比較してて???ってなったんだけど
なんてことはないcrypt関数の第2引数はsaltで必要な部分までしか参照されないのであった

ちなみにマニュアルには色々書いてあって、簡単には内容把握しづらいんだけど、用はsaltで渡された値の最初の部分を元にどのアルゴリズムを使用するかが判別されてます。

Blowfishを使いたい場合は下記のような29桁のsaltを使うことで対応できる

$[プレフィックス]$[反復回数]$[22桁のランダム文字列を"0-9a-zA-Z"で指定]

$salt = '$2y$04$abcdefghijklmnopqrstuv';
  • 反復回数
    • Blowfishアルゴリズムで使う反復回数
    • 04〜31までの値で指定できる
    • 数が大きければ複雑にハッシュ化されるが、その分時間もかかる
  • ランダム文字列部分
    • 0-9a-zA-Z の文字で構成された22桁の文字列

22桁の部分は共有しないで、ユーザー個別に生成して保持しておいたほうがなにかとよろしいのかな?

てことで、自分用のメモ