web-dev-qa-db-ja.com

のための安全なハッシュとソルト PHP パスワード

現在MD5は部分的に安全ではないと言われています。これを考慮して、パスワード保護にどのメカニズムを使用するかを知りたいのですが。

この質問、 「二重ハッシュ」は、1回ハッシュするよりも安全性が低いですか。 複数回ハッシュすることは良い考えかもしれないことを示唆しているのに対し、 個々のファイルに対してパスワード保護をどのように実装するか? 塩を使うことを提案します。

私はPHPを使っています。安全で高速なパスワード暗号化システムが必要です。パスワードを100万回ハッシュすることは安全であるかもしれませんが、また遅くなります。速度と安全性の間でどのようにバランスをとるか?また、私は結果が一定の文字数を持つことを望みます。

  1. ハッシュメカニズムはPHPで利用可能でなければなりません
  2. 安全でなければなりません
  3. それは塩を使うことができます(この場合、すべての塩は等しく良いですか?良い塩を生成する方法はありますか?)

また、データベースに2つのフィールドを格納する必要があります(1つはMD5を使用し、もう1つはSHAを使用します)。それがより安全になるのでしょうか、それとも安全にならないのでしょうか。

よくわからない場合は、安全で高速なパスワード保護メカニズムを実現するためにどのハッシュ関数を使用するのか、そしてどのようにして適切なソルトを選択するのかを知りたいです。

私の質問をあまりカバーしていない関連質問:

SHAのPHPとMD5の違いは何ですか -
簡易パスワード暗号化
asp.netのキー、パスワードを安全に保管する方法 /
Tomcat 5.5で塩味のパスワードをどのように実装しますか /

1115
luiscubal

免責事項:この回答は2008年に書かれました。

それ以来、PHPは password_hash および password_verify を提供してきました。また、導入以来、これらは推奨されるパスワードハッシュおよびチェック方法です。

答えの理論はまだ良い読み物です。

TL; DR

いけない

  • ユーザーがパスワードに入力できる文字を制限しないでください。これを行うのはバカだけです。
  • パスワードの長さを制限しないでください。ユーザーがsupercalifragilisticexpialidociousを含む文を必要とする場合、その文の使用を妨げないでください。
  • パスワードのHTMLや特殊文字を削除したりエスケープしたりしないでください。
  • ユーザーのパスワードをプレーンテキストで保存しないでください。
  • ユーザーにパスワードをメールで送信しないでください。ユーザーがパスワードを失った場合を除き、一時的なパスワードを送信します。
  • 決して、決してパスワードを記録しないでください。
  • SHA1 またはMD5またはSHA256でパスワードをハッシュしないでください! 現代のクラッカー は、600および1,800億ハッシュ/秒(それぞれ)を超えることができます。
  • bcryptとhash()raw出力を混在させないでください。16進出力を使用するかbase64_encodeしてください。 (これは、不正な\0が含まれている可能性のあるすべての入力に適用され、セキュリティを著しく低下させる可能性があります。)

ドス

  • 可能な場合はscryptを使用してください。できない場合はbcrypt。
  • SHA2ハッシュでbcryptまたはscryptを使用できない場合は、PBKDF2を使用します。
  • データベースが侵害されたときに全員のパスワードをリセットします。
  • 妥当な8〜10文字の最小長を実装し、さらに少なくとも1つの大文字、1つの小文字、数字、および記号が必要です。これにより、パスワードのエントロピーが改善され、ひび割れが難しくなります。 (いくつかの議論については、「何が良いパスワードを作るのか?」セクションを参照してください。)

とにかくパスワードをハッシュするのはなぜですか?

パスワードをハッシュする目的は単純です。データベースを危険にさらすことにより、ユーザーアカウントへの悪意のあるアクセスを防止します。そのため、パスワードハッシュの目的は、プレーンテキストのパスワードを計算するのに時間やお金をかけすぎて、ハッカーやクラッカーを阻止することです。そして、時間/コストはあなたの武器庫で最高の抑止力です。

ユーザーアカウントに適切で堅牢なハッシュが必要なもう1つの理由は、システム内のすべてのパスワードを変更するのに十分な時間を与えることです。データベースが危険にさらされている場合、データベース内のすべてのパスワードを変更しない限り、システムをロックダウンleastするのに十分な時間が必要です。

Whitehat SecurityのCTOであるJeremiah Grossman氏 彼のブログに記載されている パスワード保護をブルートフォースで破る必要がある最近のパスワード回復の後:

興味深いことに、この悪夢を生き抜く中で、パスワードのクラッキング、ストレージ、複雑さについて知らなかったことがたくさんありました。 パスワードの保存がパスワードの複雑さよりもずっと重要である理由を理解するようになりました。パスワードの保存方法がわからない場合、本当に頼ることができるのは複雑さだけですこれはパスワードと暗号の専門家にとって一般的な知識かもしれませんが、平均的なInfoSecまたはWebセキュリティの専門家にとっては、疑わしい。

(エンファシス鉱山。)

とにかくgoodパスワードを作るのは何ですか?

エントロピー 。 (ランドールの視点に完全に同意しているわけではありません。)

要するに、エントロピーとは、パスワード内の変動の大きさです。パスワードが小文字のローマ字のみの場合、それはわずか26文字です。それはそれほど大きな違いではありません。 36文字の英数字のパスワードの方が適しています。ただし、記号を使用して大文字と小文字を区別できるのは約96文字です。それは単なる手紙よりもはるかに良いです。 1つの問題は、パスワードを覚えやすくするために、パターンを挿入することです。これにより、エントロピーが減少します。おっとっと!

パスワードのエントロピーは、簡単に approximated です。 ASCII文字の全範囲(およそ96の入力可能な文字)を使用すると、1文字あたり6.6のエントロピーが得られますが、パスワードの8文字では、将来のセキュリティのために低すぎます(エントロピーの52.679ビット)。しかし、良いニュースは次のとおりです。長いパスワード、およびユニコード文字を含むパスワードは、パスワードのエントロピーを実際に増加させ、解読を困難にします。

Crypto StackExchange サイトで、パスワードエントロピーの詳細な説明があります。適切なGoogle検索でも多くの結果が得られます。

私は@popnoodlesと話したコメントで、X =長さXのパスワードポリシーをX個の文字、数字、記号などでenforcecingすると、パスワードスキームを作成することでエントロピーを実際に減らすことができると指摘しましたより予測可能。私は同意しますよ。可能な限り真にランダムなRandomessは、常に最も安全であるが記憶に残るソリューションです。

私が知る限り、世界最高のパスワードを作成することはCatch-22です。記憶できない、予測しやすい、短すぎる、Unicode文字が多すぎる(Windows /モバイルデバイスで入力するのが難しい)、長すぎるなど。目的に合ったパスワードではないため、それらを保護する必要があります。フォートノックスにいた。

ベストプラクティス

Bcryptと scrypt が現在のベストプラクティスです。 Scrypt はやがてbcryptよりも優れたものになりますが、Linux/UnixやWebサーバーによる標準としての採用は見られておらず、アルゴリズムの詳細なレビューはまだ投稿されていません。それでも、アルゴリズムの将来は有望に見えます。 Rubyを使用している場合は scrypt gem が役立ち、Node.jsには独自の scrypt パッケージがあります。 PHPでScryptを使用するには、 Scrypt 拡張機能または Libsodium 拡張機能(両方ともPECLで利用可能)を使用します。

Bcryptの使用方法を理解したい場合は crypt function のドキュメントを読むか、 goodwrapper を見つけるか、何かを使用することを強くお勧めします PHPASS のような、より古い実装の場合。 15から18でなければ、少なくとも12ラウンドのbcryptをお勧めします。

Bcryptがblowfishのキースケジュールのみを使用し、可変コストメカニズムを使用していることを知ったとき、bcryptの使用について考えを変えました。後者では、blowfishのすでに高価なキースケジュールを増やすことで、パスワードを総当たり攻撃するコストを増やすことができます。

平均的な慣行

私はもうこの状況をほとんど想像できません。 PHPASS はPHP 3.0.18から5.3をサポートするため、考えられるほとんどすべてのインストールで使用できます。知らない場合使用する必要がありますご使用の環境がbcryptをサポートしていることを特定

ただし、bcryptまたはPHPASSをまったく使用できないとします。それで何?

PDKBF2 の実装を試してください 最大ラウンド数 環境/アプリケーション/ユーザーの認識が許容できるもの。私が推奨する最低数は2500ラウンドです。また、操作を再現しにくくするために利用できる場合は、 hash_hmac() を使用するようにしてください。

今後の実践

PHP 5.5には、 完全なパスワード保護ライブラリ が含まれており、bcryptを使用する際の苦痛を取り除きます。ほとんどの一般的な環境、特に共有ホストではほとんどの人がPHP 5.2および5.3に固執していますが、@ ircmaxellは_と下位互換性のある今後のAPIのために 互換性レイヤー を構築しましたPHP 5.3.7。

暗号の要約と免責事項

ハッシュ化されたパスワードを実際にcrackするために必要な計算能力は存在しません。コンピュータがパスワードを「クラック」する唯一の方法は、パスワードを再作成し、それを保護するために使用されるハッシュアルゴリズムをシミュレートすることです。ハッシュの速度は、総当たり攻撃の能力に直線的に関連しています。さらに悪いことに、ほとんどのハッシュアルゴリズムを簡単に並列化して、さらに高速に実行できます。これが、bcryptやscryptなどの高価なスキームが非常に重要な理由です。

すべての脅威や攻撃手段を予見することはできないため、ユーザーを保護するために最大限の努力を払う必要があります前もって。そうしないと、手遅れになるまで攻撃されたという事実を見逃す可能性があります...そしてあなたは責任を負います。そのような状況を避けるために、まずは妄想的に行動してください。独自のソフトウェアを(内部で)攻撃し、ユーザーの資格情報を盗もうとしたり、他のユーザーのアカウントを変更したり、データにアクセスしたりします。システムのセキュリティをテストしなければ、自分以外の人を非難することはできません。

最後に:私は暗号作成者ではありません。私が言ったことは私の意見ですが、たまたまそれは良き昔の常識に基づいていると思います...そしてたくさんの読書。可能な限り偏執的になり、物事をできる限り侵入しにくくし、それでもまだ心配な場合は、ホワイトハットのハッカーまたは暗号作成者に連絡して、コード/システムについての発言を確認してください。

957
Robert K

はるかに短く安全な答え-独自のパスワードメカニズムをまったく記述しないでください、試行錯誤のメカニズムを使用してください。

  • PHP 5.5以降: password_hash() は高品質で、PHPコアの一部です。
  • 古いPHPバージョン:OpenWallの phpass ライブラリは、WordPress、Drupalなどで使用されるほとんどのカスタムコードよりもはるかに優れています。

ほとんどのプログラマーは、脆弱性を導入せずに暗号関連のコードを安全に作成する専門知識を持っていません。

クイックセルフテスト:パスワードストレッチとは何ですか?答えがわからない場合は、password_hash()を使用する必要があります。これは、パスワードストレッチングが、CPUの高速化とクラックのための GPUおよびFPGA の使用により、パスワードメカニズムの重要な機能になったためです 毎秒10億回の推測 (GPUを使用)のレートのパスワード。

たとえば、 6時間で8文字のすべてのWindowsパスワードを解読 5台のデスクトップPCにインストールされた25個のGPUを使用できます。これはブルートフォースです。つまり、特殊文字を含めて8文字ごとのWindowsパスワードの列挙とチェックであり、辞書攻撃ではありません。それは2012年でしたが、2018年の時点では、使用するGPUの数を減らすか、25のGPUでより速くクラックできました。

また、通常のCPUで実行される非常に高速なWindowsパスワードに対するレインボーテーブル攻撃も多数あります。これはすべて、Windows stillソルトもストレッチもしない そのパスワード、 Windows 10であっても -Microsoftと同じ間違いをしないでくださいした!

以下も参照してください:

  • 優れた答えpassword_hash()またはphpassが最適な方法である理由についての詳細。
  • 良いブログ記事 bcrypt、scrypt、PBKDF2を含む主要なアルゴリズムに推奨される「作業要素」(反復回数)を指定します。
132
RichVel

ハッシュされたパスワードを2つの異なる方法で保存することはしません。その場合、システムは少なくとも使用中の最も弱いハッシュアルゴリズムと同じくらい弱いものになります。

41
Tom Haigh

PHP 5.5以降、PHPにはパスワードのハッシュ化と検証のためのシンプルで安全な機能があります。 password_hash() および password_verify()

$password = 'anna';
$hash = password_hash($password, PASSWORD_DEFAULT);
$expensiveHash = password_hash($password, PASSWORD_DEFAULT, array('cost' => 20));

password_verify('anna', $hash); //Returns true
password_verify('anna', $expensiveHash); //Also returns true
password_verify('elsa', $hash); //Returns false

password_hash()が使用されると、ランダムなsaltを生成し、それを(使用されるコストとアルゴリズムと共に)出力されたハッシュに含めます。次に、password_verify()はそのハッシュを読み取り、使用されるsaltと暗号化方法を決定し、提供されたプレーンテキストのパスワードと照合します。 。

PASSWORD_DEFAULTを提供することはPHPにインストールされたバージョンのPHPのデフォルトのハッシュアルゴリズムを使うように指示します。正確にはどのアルゴリズムが将来のバージョンで時間とともに変化することを意図しているので、それは常に最も強力な利用可能なアルゴリズムの1つになるでしょう。

コストが上がると(デフォルトは10)、ハッシュをブルートフォースするのが難しくなりますが、ハッシュを生成し、それに対してパスワードを検証することは、サーバーのCPUにとってより多くの作業が必要になります。

デフォルトのハッシュアルゴリズムが変更されても、使用されているアルゴリズムがハッシュに格納されていてpassword_verify()がそれを取得するため、古いハッシュでも問題なく検証されます。

34

質問に答えましたが、ハッシングに使用される塩はランダムであり、最初の回答で提案された電子メールアドレスとは異なるべきであることを繰り返し述べたいと思います。

詳細な説明は、 - http://www.pivotalsecurity.com/blog/password-hashing-salt-should-it-be-random/ にあります。

最近、ランダムビットで塩漬けされたパスワードハッシュが、推測可能な塩または既知の塩で塩漬けされたハッシュよりも安全であるかどうかについて議論しました。パスワードを保存しているシステムとランダムなソルトを保存しているシステムが侵害された場合、攻撃者はソルトだけでなくハッシュにもアクセスできるため、ソルトがランダムかどうかは関係ありません。攻撃者は事前に計算されたRainbowテーブルを生成してハッシュを解読することができます。興味深い部分があります - 事前計算されたテーブルを生成することはそれほど簡単ではありません。 WPAセキュリティモデルの例を見てみましょう。 WPAパスワードは、実際にはワイヤレスアクセスポイントに送信されません。代わりに、それはあなたのSSID(Linksys、Dlinkなどのようなネットワーク名)でハッシュされます。これがどのように機能するかについての非常に良い説明がここにあります。ハッシュからパスワードを取得するには、パスワードとsalt(ネットワーク名)を知っている必要があります。 Church of Wifiはすでにトップ1000のSSIDと約100万のパスワードを持つハッシュテーブルを計算しています。すべてのテーブルのサイズは約40 GBです。あなたが彼らのサイトで読むことができるように、誰かがこれらのテーブルを生成するために3日間15のFGPAアレイを使いました。被害者がSSIDを「a387csf3」、パスワードを「123456」として使用していると仮定した場合、それらのテーブルからクラックされるのでしょうか。いいえ! .. できない。パスワードが弱い場合でも、テーブルにはSSID a387csf3のハッシュはありません。これはランダム塩を持つことの美しさです。それは事前に計算されたテーブルで繁栄するクラッカーを阻止するでしょう。それは断固としたハッカーを阻止できますか?おそらくそうではありません。しかし、ランダムな塩を使用することは追加の防御層を提供します。私たちがこのトピックについて話している間に、ランダムな塩を別のシステムに保存することの追加の利点を議論しましょう。シナリオ#1:パスワードハッシュはシステムXに保存され、ハッシュに使用されるソルト値はシステムYに保存されます。これらのソルト値は推測可能または既知です(ユーザー名など)ハッシュはシステムYに保存されます。これらのsalt値はランダムです。ご想像のとおり、システムXが危険にさらされている場合、ランダムソルトを別のシステムで使用することには大きな利点があります(シナリオ#2)。攻撃者はハッシュを解読できるようにするために付加価値を推測する必要があります。 32ビットのソルトが使用されている場合、推測されたパスワードごとに2 ^ 32 = 4,294,967,296(約42億)の反復が必要になる可能性があります。

31
Gaurav Kumar

PHP 5.5には、crypt()のラッパーを提供する パスワードハッシュ化API が含まれています。このAPIは、パスワードハッシュのハッシュ化、検証、およびハッシュ化のタスクを大幅に簡素化します。 PHP 5.3.7以降を使っている人のために、作者は 互換性パック (単一のpassword.phpファイルをあなたが使うためのrequirename__の形で)もリリースしました。今これを使用する。

現在のところBCRYPTのみをサポートしていますが、他のパスワードハッシュ技術を含むように簡単に拡張することを目的としています。また、その技術とコストはハッシュの一部として格納されています。検証時には自動的に正しいテクニック/コストを使用します。あなたが明示的にあなた自身のものを定義しないならば、それはまた「安全な」ソルトの生成を扱います。

APIは4つの機能を公開します。

  • password_get_info() - 与えられたハッシュに関する情報を返します
  • password_hash() - パスワードハッシュを作成します
  • password_needs_rehash() - 与えられたハッシュが与えられたオプションと一致するかどうかチェックします。ハッシュが現在のテクニック/コスト体系に準拠しているかどうかを確認するのに役立ちます。
  • password_verify() - パスワードがハッシュと一致することを確認します

現時点でこれらの関数はPASSWORD_BCRYPTとPASSWORD_DEFAULTのパスワード定数を受け付けます。これらは現時点では同義語ですが、PASSWORD_DEFAULTは "新しいもので変更される可能性があります PHP ログイン時にPASSWORD_DEFAULTとpassword_needs_rehash()を使用すると(そして必要であれば再ハッシュすると)、ハッシュはブルートフォース攻撃に対して適度に回復力があり、ほとんど機能しません。

編集:私はちょうどこれがロバートKの答えで簡単に言及されていることに気づいた。それがどのように機能するかについてのもう少しの情報とそれがセキュリティを知らない人々に提供する使いやすさについて提供すると思うので、私はこの答えをここに残します。

26
JonoCoetzee

私は Phpass を使用しています。これは単純な1ファイルPHPクラスで、ほぼすべてのPHPプロジェクトに非常に簡単に実装できます。 The H も参照してください。

デフォルトではPhpassに実装されている最強の利用可能な暗号化を使用しました。これはbcryptであり、Wordpressのようなフレームワークへの下位互換性を提供するためにMD5までの他の暗号化に戻ります。

返されたハッシュはそのままデータベースに格納できます。ハッシュを生成するための使用例は次のとおりです。

$t_hasher = new PasswordHash(8, FALSE);
$hash = $t_hasher->HashPassword($password);

パスワードを確認するには、次のようにします。

$t_hasher = new PasswordHash(8, FALSE);
$check = $t_hasher->CheckPassword($password, $hash);
18
rabudde

覚えておくべきこと

PHPのパスワード暗号化については多くのことが言われていますが、その大部分は非常に良いアドバイスですが、PHPをパスワード暗号化に使用するプロセスを始める前に、次のものを実装するか実装する準備をしてください。

_サーバー_

PORTS

PHPとDBを実行するサーバーを適切に保護しなければ、暗号化がどれほど優れていても、すべての努力は無意味です。ほとんどのサーバーは比較的同じように機能します。それらには、ftpまたはシェルを介してリモートからアクセスできるように割り当てられたポートがあります。必ずリモート接続をアクティブにしているデフォルトポートを変更してください。これをしないことによって、あなたは事実上攻撃者があなたのシステムにアクセスすることにおいてより少ない1ステップをするようにした。

USERNAME

世の中で良いことはすべて、ユーザー名admin、root、またはこれに類するものを使用しないでください。また、あなたがUNIXベースのシステムを使っているのであれば、rootアカウントのログインをアクセス可能にしないでください、それは常にSudoだけであるべきです。

PASSWORD

ハッキングされないように、良いパスワードを入力するようにユーザーに伝えます。あなたがバックドアを大きく開いているときにあなたのフロントドアをロックするすべての努力を経験することのポイントは何ですか。

_データベース_

SERVER

理想的には、DBとアプリケーションを別々のサーバーに配置します。これはコスト上必ずしも可能ではありませんが、攻撃者がシステムに完全にアクセスするには2つのステップを経なければならないため、ある程度の安全性が得られます。

USER

アプリケーションには常にDBにアクセスするための独自のアカウントを持たせ、必要な権限だけを与えてください。

それから、アプリケーション内でもなく、サーバー上のどこにも保存されていない別のユーザーアカウントを持っています。

いつものように、この根や何かを似たものにしないでください。

PASSWORD

すべての優れたパスワードと同じガイドラインに従ってください。また、同じシステム上のどのSERVERアカウントまたはDBアカウントにも同じパスワードを使用しないでください。

_ php _

PASSWORD

パスワードをDBに決して保存せず、代わりにハッシュと一意のsaltを保存します。後で説明します。

ハッシュ

一方向のハッシュ!!!!!!!、パスワードを元に戻すことができる方法でハッシュしないでください。ハッシュは一方向にする必要があります。つまり、パスワードを元に戻してパスワードと比較しないでください。同じ方法で、2つのハッシュを比較します。これは、たとえ攻撃者がDBにアクセスしたとしても、実際のパスワードが何であるかを知らず、結果として生じるハッシュだけを知ることを意味します。これは、可能な限り最悪のシナリオでもユーザーにとってより高いセキュリティを意味します。

たくさんの良いハッシュ関数(password_hashhashなど)がありますが、ハッシュを効果的にするためには良いアルゴリズムを選択する必要があります。 (bcryptとそれに似たものはまともなアルゴリズムです。)

ハッシュ速度が重要な場合は、速度が遅いほどブルートフォース攻撃に対する耐性が強くなります。

ハッシュの最も一般的な間違いの1つは、ハッシュがユーザーに固有のものではないということです。これは主に塩が一意に生成されないためです。

塩味

パスワードはハッシュする前に必ず塩漬けにする必要があります。 Saltingはパスワードにランダムな文字列を追加するので、似たようなパスワードはDBに同じには見えません。ただし、saltが各ユーザーに固有のものではない場合(つまり、ハードコーディングされたsaltを使用する場合)、ほとんどの場合、saltは価値がなくなります。攻撃者が1つのパスワードソルトを見つけたら、彼はそれらすべてのソルトを持っているからです。

あなたがsaltを作成するとき、それがsaltしているパスワードに固有のものであることを確認し、そしてあなたのDBに完成したハッシュとsaltの両方を保存してください。これが何をするかは、攻撃者がアクセスする前にそれぞれのsaltとhashを個別に解読しなければならないようにすることです。これは攻撃者にとってより多くの作業と時間を意味します。

ユーザー作成パスワード

ユーザーがフロントエンドでパスワードを作成している場合、それはサーバーに送信する必要があることを意味します。暗号化されていないパスワードがサーバーに送信され、攻撃者がそれを監視してアクセスできる場合、PHP内のすべてのセキュリティは価値がないため、セキュリティ上の問題が発生します。常に安全にデータを送信します。これはSSLを介して行われますが、SSLに問題がない場合でも注意してください(OpenSSLのHeartbleed欠陥はその一例です)。

また、ユーザーに安全なパスワードを作成させる、それは簡単で常に行われるべきです、ユーザーは結局それに感謝するでしょう。

最後に、あなたが何もしないセキュリティ対策が100%安全であるとしても、保護するための技術がより高度になればなるほど攻撃はより高度になる。しかし、これらのステップに従うことはあなたのサイトをより安全にし、攻撃者がそれを追いかけるのをあまり望ましくないでしょう。

これはパスワードのハッシュとsaltを簡単に作成するPHPクラスです。

http://git.io/mSJqpw

13
wmfrancia

Googleによると、SHA256はPHPで利用可能です。

あなたは間違いなく塩を使うべきです。私はランダムなバイトを使うことをお勧めします(そして自分自身を文字と数字に制限しないでください)。いつものように、長く選ぶほど、安全になり、遅くなります。 64バイトは問題ないはずです。

12
AticusFinch

私はここでこの問題に関する完璧なトピックを見つけました: https://crackstation.net/hashing-security.htm 、私はあなたにそれから利益を得てほしかったです、ここにまた時間ベースの攻撃に対する予防を提供したソースコードがあります。

<?php
/*
 * Password hashing with PBKDF2.
 * Author: havoc AT defuse.ca
 * www: https://defuse.ca/php-pbkdf2.htm
 */

// These constants may be changed without breaking existing hashes.
define("PBKDF2_HASH_ALGORITHM", "sha256");
define("PBKDF2_ITERATIONS", 1000);
define("PBKDF2_SALT_BYTES", 24);
define("PBKDF2_HASH_BYTES", 24);

define("HASH_SECTIONS", 4);
define("HASH_ALGORITHM_INDEX", 0);
define("HASH_ITERATION_INDEX", 1);
define("HASH_SALT_INDEX", 2);
define("HASH_PBKDF2_INDEX", 3);

function create_hash($password)
{
    // format: algorithm:iterations:salt:hash
    $salt = base64_encode(mcrypt_create_iv(PBKDF2_SALT_BYTES, MCRYPT_DEV_URANDOM));
    return PBKDF2_HASH_ALGORITHM . ":" . PBKDF2_ITERATIONS . ":" .  $salt . ":" . 
        base64_encode(pbkdf2(
            PBKDF2_HASH_ALGORITHM,
            $password,
            $salt,
            PBKDF2_ITERATIONS,
            PBKDF2_HASH_BYTES,
            true
        ));
}

function validate_password($password, $good_hash)
{
    $params = explode(":", $good_hash);
    if(count($params) < HASH_SECTIONS)
       return false; 
    $pbkdf2 = base64_decode($params[HASH_PBKDF2_INDEX]);
    return slow_equals(
        $pbkdf2,
        pbkdf2(
            $params[HASH_ALGORITHM_INDEX],
            $password,
            $params[HASH_SALT_INDEX],
            (int)$params[HASH_ITERATION_INDEX],
            strlen($pbkdf2),
            true
        )
    );
}

// Compares two strings $a and $b in length-constant time.
function slow_equals($a, $b)
{
    $diff = strlen($a) ^ strlen($b);
    for($i = 0; $i < strlen($a) && $i < strlen($b); $i++)
    {
        $diff |= ord($a[$i]) ^ ord($b[$i]);
    }
    return $diff === 0; 
}

/*
 * PBKDF2 key derivation function as defined by RSA's PKCS #5: https://www.ietf.org/rfc/rfc2898.txt
 * $algorithm - The hash algorithm to use. Recommended: SHA256
 * $password - The password.
 * $salt - A salt that is unique to the password.
 * $count - Iteration count. Higher is better, but slower. Recommended: At least 1000.
 * $key_length - The length of the derived key in bytes.
 * $raw_output - If true, the key is returned in raw binary format. Hex encoded otherwise.
 * Returns: A $key_length-byte key derived from the password and salt.
 *
 * Test vectors can be found here: https://www.ietf.org/rfc/rfc6070.txt
 *
 * This implementation of PBKDF2 was originally created by https://defuse.ca
 * With improvements by http://www.variations-of-shadow.com
 */
function pbkdf2($algorithm, $password, $salt, $count, $key_length, $raw_output = false)
{
    $algorithm = strtolower($algorithm);
    if(!in_array($algorithm, hash_algos(), true))
        die('PBKDF2 ERROR: Invalid hash algorithm.');
    if($count <= 0 || $key_length <= 0)
        die('PBKDF2 ERROR: Invalid parameters.');

    $hash_length = strlen(hash($algorithm, "", true));
    $block_count = ceil($key_length / $hash_length);

    $output = "";
    for($i = 1; $i <= $block_count; $i++) {
        // $i encoded as 4 bytes, big endian.
        $last = $salt . pack("N", $i);
        // first iteration
        $last = $xorsum = hash_hmac($algorithm, $last, $password, true);
        // perform the other $count - 1 iterations
        for ($j = 1; $j < $count; $j++) {
            $xorsum ^= ($last = hash_hmac($algorithm, $last, $password, true));
        }
        $output .= $xorsum;
    }

    if($raw_output)
        return substr($output, 0, $key_length);
    else
        return bin2hex(substr($output, 0, $key_length));
}
?>
8
Jason OOO

結局、数学的には、二重ハッシュは利点をもたらさない。しかし実際には、Rainbowのテーブルベースの攻撃を防ぐのに役立ちます。言い換えれば、アプリケーションやサーバーのプロセッサ時間が大幅に短縮されるという利点があります。

7
Max

私は通常SHA1とsaltをユーザーID(あるいは他のユーザー固有の情報)と一緒に使いますが、時にはさらに定数のsaltを使うことがあります(そのためsaltに2つの部分があります)。

SHA1も現在やや妥協されていると見なされていますが、MD5よりはるかに低い程度です。ソルト(任意のソルト)を使用することで、ハッシュを攻撃するための一般的な Rainbow table の使用を防ぎます(ハッシュを検索してGoogleを一種のRainbow tableとして成功させることさえありました)。攻撃者があなたのsaltを使ってRainbowテーブルを生成する可能性があるので、ユーザー固有のsaltを含めるべきです。そうすれば、システム全体のレコードだけでなく、システムの各レコードごとにRainbowテーブルを生成する必要があります。この種の塩漬けでは、MD5でもきちんと安全です。

6
rmeador

近いうちに SHA1 とsaltで十分なはずです(当然のことながら、 Fort Knox 用にコーディングするのか、ログインリスト用にログインシステム用にコーディングするのかによって異なります)。 SHA1があなたにとって十分ではない場合、 SHA256 を使用してください。

ソルトのアイデアは、ハッシュ結果をバランスよくずらすことです。たとえば、空の文字列のMD5ハッシュはd41d8cd98f00b204e9800998ecf8427eであることが知られています。したがって、十分なメモリを持っている人がそのハッシュを見て、それが空の文字列のハッシュであることを知っているとします。しかし、その文字列が塩味を帯びている場合(たとえば、文字列 "MY_PERSONAL_SALT"の場合)、 '空の文字列'のハッシュ(つまり、 "MY_PERSONAL_SALT")はaeac2612626724592271634fb14d3ea6になるため、バックトレースするのはわかりません。私が言おうとしているのは、 any saltを使用するのではなく、使用するほうがよいということです。したがって、 which saltを知ることはそれほど重要ではありません。

実際には これを実現しているWebサイト があります - あなたはそれに(md5)ハッシュを入れることができます、そしてそれはその特定のハッシュを生成する既知の平文を吐き出します。プレーンなmd5ハッシュを格納しているデータベースにアクセスするのであれば、そのようなサービスに管理者用のハッシュを入力してログインするのは簡単です。しかし、パスワードが変更されている場合、そのようなサービスは無効です。

また、ダブルハッシングは結果空間を縮小するので、一般的に悪い方法と見なされています。一般的なハッシュはすべて固定長です。したがって、この固定長の有限値だけを持つことができ、結果はそれほど変化しなくなります。このにすることができますが、私はそれをお勧めしません。

4
Henrik Paul