ハッシュと安全なパスワードストレージについて少し

更新しました。 BCryptが何であるかを知っていれば、もう読むことができません。 PHP 5.5以降を使用している場合は、 この記事を読むことができます。 以下では、作業者である自転車を発明しましたが、2つのハンドルバーと後部スペアを備えています。 彼は若くて暑かった。



こんにちは、Habr! 今日、プロジェクトの認証システムを開発する過程で、データベースにユーザーパスワードを保存する方法を選択しました。 多くのオプションが思い浮かびます。 最も明白な:







もちろん、最初のオプションはすぐになくなりました。 いくつかの審議の後に通常のハッシュを使用することも、いくつかの理由で廃棄する必要がありました。



ハッシュ関数


ハッシュ関数の衝突は、異なる入力データで同じ結果を生成するときに発生します。 もちろん、これの可能性は非常に小さく、ハッシュの長さに依存します。 ただし、廃止された(しかしまだ使用されている)関数crc32()は、32ビット整数をハッシュとして返します。 つまり、そのようなハッシュのパスワードを取得するには、確率理論に従って、2 ^ 32 = 4,294,967,296個の異なるハッシュを取得する必要があります。 私の無料ホスティングでもcrc32は毎秒350,000回の速度で動作します-そのようなハッシュをクラックするのに何秒かかるかを自分で計算してください;)



もちろん、これはmd5()(128ビットハッシュ)、特にsha1()(160ビットハッシュ)には適用されません。 それらの衝突を使用することはほとんど不可能ですが、 1つの小さな記事があります...



レインボーテーブル


レインボーテーブルは、名前、生年月日、動物名など、最も一般的に使用されるパスワードのハッシュで構成されています。 これらのテーブルには数百万、数十億の値を含めることができますが、それらの操作は比較的迅速であり、値の1つに対するハッシュのチェックは簡単です。 一部には、「塩」またはmd5(sha1(md5($ pass)))のような構造の使用から保護できます。



$password = "easypassword"; //  ,   , ,     echo sha1($password); //       sha1()  : 6c94d3b42518febd4ad747801d50a8972022f956 $salt = "f#@V)Hu^%Hgfds"; //    ,      echo sha1($salt . $password); //     ,  : cd56a16759623378628c0d9336af69b74d9d71a5 //             
      
      







レインボーテーブル。 パート2


静的な塩などは、これらの構造と塩の構造が秘密に保たれている限り、非常にうまく機能します。 攻撃者がハッシュシークレットを検出すると、その「レインボーテーブル」を簡単に変更できます。 そして以来 サーバーのセキュリティシステムに完全に依存することはできません。別のオプションを探す必要があります。 1つの解決策は、次のようなユーザーごとに一意のソルトを生成することです。



 $hash = sha1($user_id . $password);
      
      







さらに良いことに、次のように完全にランダムなソルトを生成します。



 //      22  function unique_salt() { return substr(sha1(mt_rand()),0,22); } $unique_salt = unique_salt(); $hash = sha1($unique_salt . $password); //   
      
      







もちろん、一意のソルトをデータベースに追加する必要がありますが、アクセスした後でも、攻撃者が数百万個のレインボーテーブルを生成できる可能性は低いです。



ハッシュレート


それは思われる-速いほど良い。 ハッシュがより速く生成されると、ユーザーはより早く登録して既に利益を上げ始めることができます。 ただし、ハッシュレートが高いほど、ハッカーはそれをより速く取得できます。



強力なGPUを搭載した最新のPCは、1秒あたり数百万以上のハッシュを計算できます。 これにより、ブルートフォース攻撃を使用して、簡単な選択でパスワードを破ることができます。 8文字のパスワードで十分だと思いますか? パスワードが小文字と大文字の文字と数字を使用する場合、可能な文字の総数は62(26 + 26 + 10)です。 8文字のパスワードの場合、62 ^ 8の異なる組み合わせ(218兆のオーダー)があります。 1秒あたり10億回のハッシュ(ブルートフォース攻撃に十分な小ささ)の速度では、パスワードは約60時間で破られます。 そして、6文字の最も一般的なパスワードの長さの場合、解読期間は2分未満です。



もちろん、短くて単純なパスワードを使用するユーザーを無視したり、句読点やシュメールの楔形文字を使用して、10文字のパスワードを自発的かつ強制的に使用するように全員に強制したりできます。 ただし、低速のハッシュ関数を使用することをお勧めします。 たとえば、次のコードを使用して、ハッシュ関数を手動で1000倍遅くすることができます。



 function myhash($password, $unique_salt) { $salt = "f#@V)Hu^%Hgfds"; $hash = sha1($unique_salt . $password); //      1000 ,     1000 ,      for ($i = 0; $i < 1000; $i++) { $hash = sha1($hash); } return $hash; }
      
      







ハッカーはこれを使用して、60時間ではなく、約7年間8文字のパスワードを解読します。 速度を落とすより便利な方法は、crypt()を介してPHPに実装されたBlowfishアルゴリズムを使用することです。 if(CRYPT_BLOWFISH == 1)echo 'it works!';でこのアルゴリズムの可用性を確認できます。 PHP 5.3では、Blowfishはすでに含まれています。



 function myhash($password, $unique_salt) { //   blowfish     22  return crypt($password, '$2a$10$'.$unique_salt); }
      
      







$ 2aは、Blowfishアルゴリズムが使用されることを示しています

10ドルは、機能を遅くする力です。 この場合、2 ^ 10です。 04から31までの値を取ることができます



具体的な例で使用します。



 $hash = '$2a$10$dfda807d832b094184faeu1elwhtR2Xhtuvs3R9J1nfRGBCudCCzC'; $password = "verysecret"; if (check_password($hash, $password)) { echo " !"; } else { echo " !"; } function check_password($hash, $password) { //  29  ,  , « »   «»    $full_salt $full_salt = substr($hash, 0, 29); //  -   $password $new_hash = crypt($password, $full_salt); //   («»  «») return ($hash == $new_hash);
      
      







このようなコードは最大限のセキュリティを提供する必要があります-通常の複雑さと長さのパスワードを選択することはほとんど不可能です(もちろん、ソフトウェアの方法を使用して)。



All Articles