QA@IT

PHPでのパスワードの暗号化について

4006 PV

お世話になっております。PHPでのパスワードの暗号化について悩んでおります。

こちらのPHPのマニュアル、「PHP:パスワードのハッシュ - Manual」
http://www.php.net/manual/ja/faq.passwords.php

を読みまして、crypt()を使うのが良いのかと思ったのですが、
以下の「Webアプリでパスワード保護はどこまでやればいいか」と言うスライドを見まして、本当にこの関数を使っていいのか迷っております。
http://www.slideshare.net/ockeghem/how-to-guard-your-password

このスライドではストレッチングというハッシュの計算の繰り返しをするのが良いとあり、hash()を使って1,000回繰り返していました。

crypt()のマニュアルには、
http://www.php.net/manual/ja/function.crypt.php

  • 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_SHA256 - SHA-256 ハッシュに $5$ で始まる 16 文字の salt を組み合わせたもの。salt 文字列が 'rounds=$' で始まる場合は、数値 N がハッシュループの実行回数を表します。 これは Blowfish のコストパラメータのようなものです。 rounds のデフォルトは 5000 で、1000 から 999,999,999 までの値を指定できます。 この範囲外の N を指定すると、近い方の境界値に切り詰められます。
  • CRYPT_SHA512 - SHA-512 ハッシュに $6$ で始まる 16 文字の salt を組み合わせたもの。salt 文字列が 'rounds=$' で始まる場合は、数値 N がハッシュループの実行回数を表します。 これは Blowfish のコストパラメータのようなものです。 rounds のデフォルトは 5000 で、1000 から 999,999,999 までの値を指定できます。 この範囲外の N を指定すると、近い方の境界値に切り詰められます。

と書いてありました。この説明にある「コストパラメータ」や「ハッシュループの実行回数」を設定すれば、ストレッチングを行うということになるのでしょうか。いまいち理解できなかったので、ご教授いただければ幸いです。

どうぞよろしくお願いいたします。

  • 判断に迷うならライブラリを使ったほうがよいと思います。PHP 5.5 で password_hash と password_verify を導入した開発者が互換ライブラリ(ircmaxell/password_compat)およびさまざまなライブラリ (Zend\Crypt\Password\Bcrypt、PasswordLib、PHPASS) のコードスニペットを公開しています。 -
  • リンクはこちらです。http://stackoverflow.com/a/17073604/531320 -
  • masakielasticさん。ありがとうございます。
    かように便利なものが...可搬性や信頼性を考えると用意されたものを使うのが一番だと思っているのでこれを使えれば使いたいと思います。
    -

回答

本当にこの関数を使っていいのか迷っております。

ここに関してはちょっと回答仕様がありませんので自己判断でお願いします。

この説明にある「コストパラメータ」や「ハッシュループの実行回数」を設定すれば、ストレッチングを行うということになるのでしょうか。

ストレッチングって「何を」なのかにもよりますけど、
ブルートフォース対策の時間だけのストレッチングを求めるならそうなります(でもここで読むのやめないでください、間違ってるから)。

実際に動作させてみればわかりますが、コストパラメータを 1増やすたびに時間が倍かかることが期待されます。これは

2 桁のコストパラメータは反復回数の 2 を底とする対数で、

とありますので、増やすたびに倍々ゲームになりますね。
roundsもまた

Blowfish のコストパラメータの ような ものです。

とありますので、回数に関するものです。こちらは回数そのままみたいですね。

さて、スライドにあるようにアプリケーション側で対処しておくメリットとしては

  • 他の方式に変えても対応できる。
  • 気が向いたらその回数もソルトに加えられる。
  • 実はそもそもハッシュ値のストレッチングのサンプルコード。

があげられます。

最後のですが、ループを見ればわかるようにループの度に$hashの値を上書きしています。
つまり何回繰り返すかによって答えが変わります。その回数繰り返さなければ答えが求まりません。ストレッチしてるのは時間じゃないということです。
CRYPTがどうなるかは試してみればわかると思います(試してませんのでどっちかわかりません。よかったら試してみて教えてください)が、スライドのサンプルコードは単純な時間稼ぎではないわけです。
英wikipediaですが以下のサンプルコードと同じですね(シンプルとか、より良いに要出典ついてるのはなんなんだろう)。
http://en.wikipedia.org/wiki/Key_stretching

オンラインでの総当たり攻撃にかかる時間を現実的でなくすることだけが目的の場合は、 sleepでもストレッチでループでもコストパラメータでも変わらないでしょう。
しかしスライドに「手段なども含め全てばれてる前提で考える」とあったと思います。そこも踏まえて考えてみてください。

※ セキュリティ絡みだと回答するのもちょっと怖いですね・・・。

編集 履歴 (0)
  • せっかくご回答いただいたのにメールを見落として気がついておりませんでした。お返事が遅くなり大変失礼致しました。
    SHA-256で試してみたのですが、crypt()もちゃんとハッシュ値が変わってはいたので繰り返しのようなことをしているんだろうと思うのですが、結局仕様はソースでも見ないとわからない気がして来ました。
    結局トレードオフを自分で考えるべきなんですね。
    どうもありがとうございました
    -
  • こちらこそ、試行していただいてありがとうございました。 -
ウォッチ

この質問への回答やコメントをメールでお知らせします。