概要
PHPのcrypt関数は、ソルト付きハッシュ値を簡単に求めることができます(公式リファレンス)。crypt関数のハッシュアルゴリズムとしてMD5を指定した場合、ソルトのみが出力され、ハッシュ値が空になります。これは、crypt関数の結果がソルトのみに依存し、パスワードには影響されないことを意味し、crypt関数を認証に用いている場合、任意のパスワードでログインに成功する可能性があります。影響を受けるアプリケーション
crypt関数を用い、ハッシュアルゴリズムとしてMD5を指定しているアプリケーション。環境にも依存しますが、デフォルトがMD5の場合もあります。筆者のテスト環境(CentOS5.6上のPHP5.1.6およびUbutu 10.04上のPHP5.3.7)では、デフォルトでMD5でした。
解説
crypt関数はパスワードと、省略可能なソルトを引数にとり、ソルトおよびハッシュ値をつなげたものを出力します。string crypt ( string $str [, string $salt ] )まず、脆弱性のない正常系を説明するために、一番簡単な呼び出し例を以下に示します。
var_dump(crypt('pass'));実行結果で、$1$はハッシュアルゴリズムがMD5であること、その次の「tggeeij6」はcrypt関数の内部で生成したソルト、「gtuAR4G2wT9XKWCuLxgcc.」はソルト付きハッシュ値です。ユーザ登録およびパスワード変更の際、上記のように呼び出したcrypt関数の結果をDBに保存します。
【実行結果】
string(34) "$1$tggeeij6$gtuAR4G2wT9XKWCuLxgcc."
認証の際のパスワード照合は以下のようにします。まず、ログイン画面で指定されたユーザに対応するハッシュ値をDBから取り出し、そこから、ハッシュアルゴリズムとソルトまでの部分を切り出します。上記の例では、「$1$tggeeij6」が該当します。次に、利用者の指定したパスワードを用いて、crypt関数を呼び出します。
var_dump(crypt('pass', '$1$tggeeij6'));この値と、DBに保存されたハッシュ値を比較し、同一であれば認証成功です。上記例では、両者が一致しているので、認証成功になります。
【実行結果】
string(34) "$1$tggeeij6$gtuAR4G2wT9XKWCuLxgcc."
一方、パスワードとして「hoge」が指示された場合を以下に示します。
var_dump(crypt('hoge', '$1$tggeeij6'));今度は、DBに保存されたものと違う結果になりましたので、認証は失敗と言うことになります。
【実行結果】
string(34) "$1$tggeeij6$kCvgMXx7rTKbrciqtQYDs1"
次に、PHP5.3.7での結果を以下に示します。まず、パスワードのみを指定した場合です。
var_dump(crypt('pass'));ご覧のように、ソルトまでの部分しか返っておらず、肝心のハッシュ値がありません。この値をDBに保存したと仮定して、次にユーザが認証時にパスワード「hoge」を指定した場合を以下に示します。
【実行結果】
string(11) "$1$BOF.kMcm"
var_dump(crypt('hoge', '$1$BOF.kMcm'));パスワードの違いは反映されず、DB上のハッシュ値(実はハッシュは空)とcrypt関数の結果は一致します。認証プログラムの実装にもよりますが、最悪のケースでは「任意のパスワードで認証可能」という状況になります。
【実行結果】
string(11) "$1$BOF.kMcm"
データベース上のハッシュ値がPHP5.3.6以前の環境で生成された場合、利用者が正しいパスワードを指定してもログインできないという状況になります。こちらは実装に依存しません。
対策
現在この問題を改修したPHP5.3.8を準備中とのことですが、当面のあいだPHP5.3.6にロールバックすることを推奨します。PHP5.3.8が出た後でも、試験環境などでテストを十分に実施するか、しばらく安定度を確認してからPHP5.3.8に移行することを推奨します。PHP5.3.6にロールバックするまでの間、あるいはPHP5.3.6にロールバックできない場合、上記に該当するWebアプリケーションは、問題が改修されるまでサービスを停止するべきです。
また、認証用のデータベースを確認して、ハッシュ値が空である利用者がいれば、パスワードのリセットが必要となります。
0 件のコメント:
コメントを投稿