The security issue with simple hashing (md5 et al) isn't really the speed, so much as the fact that it's idempotent; two different people with the same password will have the same hash, and so if one person's hash is brute-forced, the other one will as well. This facilitates rainbow attacks. Simply slowing the hash down isn't a very useful tactic for improving security. It doesn't matter how slow and cumbersome your hash algorithm is - as soon as someone has a weak password that's in a dictionary, EVERYONE with that weak password is vulnerable.
Also, hash algorithms such as md5 are for the purpose of generating a digest and checking if two things are probably the same as each other; they are not intended to be impossible to generate a collision for. Even if an underlying password itself requires a lot of brute forcing to determine, that doesn't mean it will be impossible to find some other bit pattern that generates the same hash in a trivial amount of time.
As such: please, please, PLEASE only use salted hashes for password storage. There is no reason to implement your own salted hash mechanism, either, as crypt() already does an excellent job of this.
安全なパスワードハッシュ
この節では、なぜハッシュ関数を使ってパスワードを守るのかについての理由と、 ハッシュ処理を効率的に行う方法について説明します。
- なぜ、アプリケーションのユーザーが登録したパスワードをハッシュしなければならないのですか?
-
パスワードのハッシュは、最も基本的なセキュリティ要件のひとつです。 ユーザーからパスワードを受け取るアプリケーションを設計するときには必ず考慮しなければなりません。 ハッシュしなければ、パスワードを格納したデータベースが攻撃を受けたときにパスワードを盗まれてしまいます。 それは即時にアプリケーションが乗っ取られることにつながるし、 もしそのユーザーが他のサービスでも同じアカウント・同じパスワードを使っていればさらに被害が大きくなります。
ユーザーのパスワードにハッシュアルゴリズムを適用してからデータベースに格納しておくと、 攻撃者が元のパスワードを知ることが難しくなります。 とはいえ、パスワードのハッシュ結果との比較は可能です。
しかし、ここで注意すべき点は、パスワードのハッシュ処理はあくまでもデータベースへの不正アクセスからの保護にすぎず、 アプリケーション自体に不正なコードを注入される攻撃からは守れないということです。
- よく使われるハッシュ関数である md5() や sha1() は、なぜパスワードのハッシュに適していないのですか?
-
MD5 や SHA1 そして SHA256 といったハッシュアルゴリズムは、 高速かつ効率的なハッシュ処理のために設計されたものです。 最近のテクノロジーやハードウェア性能をもってすれば、 これらのアルゴリズムの出力をブルートフォースで(力ずくで)調べて元の入力を得るのはたやすいことです。
最近のコンピュータではハッシュアルゴリズムを高速に「逆算」できるので、 セキュリティ技術者の多くはこれらの関数をパスワードのハッシュに使わないよう強く推奨しています。
- よく使われるハッシュ関数では不適切だというのなら、 パスワードをどうやってハッシュすればいいのですか?
-
パスワードをハッシュするときに検討すべき重要な二点は、 その計算量とソルトです。 ハッシュアルゴリズムの計算コストが増えれば増えるほど、 ブルートフォースによる出力の解析に時間を要するようになります。
PHP には、 使うアルゴリズムを指定してハッシュを実行できる関数がふたつ組み込まれています。
まず最初のハッシュ関数は crypt() で、これはネイティブで数種のハッシュアルゴリズムに対応しています。 この関数を使うときには、選択したアルゴリズムが使用可能なことが保証されています。 PHP には各アルゴリズムのネイティブ実装が含まれているので、 仮にシステムがサポートしていない場合でもそのアルゴリズムを使えるのです。
もうひとつのハッシュ関数は hash() で、 これは crypt() よりもさらに多数のアルゴリズムやその亜種に対応しています。 しかし、 crypt() が対応しているアルゴリズムのいくつかには未対応です。 Hash 拡張モジュールは PHP に同梱されていますが、コンパイル時に無効化することもできます。 したがって、常に使えるとは限りません。一方 crypt() は PHP のコアに含まれているので、いつでも使えます。
パスワードのハッシュ用として推奨するアルゴリズムは Blowfish です。 これは MD5 や SHA1 に比べて圧倒的に計算量が多く、それでいてスケーラブルだからです。
- ソルトとは?
-
暗号理論におけるソルトとは、ハッシュ処理の際に追加するデータのことです。 事前に計算済みのハッシュとその元入力の対応表 (レインボーテーブル) で出力を解析される可能性を減らすために利用します。
端的に言うと、ソルトとはちょっとした追加データです。 これをつけるだけで、ハッシュをクラックするのが劇的に難しくなります。 事前に計算済みのハッシュとその元入力を大量にまとめた表が、オンラインで多数公開されています。 ソルトを使えば、そのハッシュ値がこれらの表に含まれている可能性を大きく減らすことができます。
sha1 in conjunction with one or more salt values need not be as insecure as the above makes it out to be. e.g. the Fossil SCM creates an sha1 password hash based on a user's clear-text password combined with the user's name and a shared secret known only to the specific source repository for which the user is set up.
For those wishing to increase the computational cost of brute-forcing their password hashes by iterating the hash command multiple times, but don't want to increase the risk of a hash collision, simply re-append the password to the hash each iteration.
<?php
$iterations = 10;
$hash = crypt($password,$salt);
for ($i = 0; $i < $iterations; ++$i)
{
$hash = crypt($hash . $password,$salt);
}
?>
This, of course, can be used with md5(), sha1(), etc. as well as crypt().
Some other recommendations:
- Use the highest number of iterations possible without introducing a significantly noticeable delay to authenticating users, or causing more CPU use than your host will allow.
- Use a unique salt for each user, ideally a random one.
- Use a salt of at least 24 bytes, especially if you're using a weaker algorithm like MD5 or SHA-1.
