22

この関数 (部分的に ~2 年前の phpBB バージョンから取得したもの) で十分かどうか疑問に思っています。

そうでない場合、なぜですか?
また、どのように変更しますか (既存のユーザーがシームレスに移行できるようにします) ?

hash_pwd() の結果が DB に保存されます。

function hash_pwd($password)
{
    $itoa64 = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';

    $random_state = $this->unique_id();
    $random = '';
    $count = 6;

    if (($fh = @fopen('/dev/urandom', 'rb')))
    {
        $random = fread($fh, $count);
        fclose($fh);
    }

    if (strlen($random) < $count)
    {
        $random = '';

        for ($i = 0; $i < $count; $i += 16)
        {
            $random_state = md5($this->unique_id() . $random_state);
            $random .= pack('H*', md5($random_state));
        }
        $random = substr($random, 0, $count);
    }

    $hash = $this->_hash_crypt_private($password, $this->_hash_gensalt_private($random, $itoa64), $itoa64);

    if (strlen($hash) == 34)
    {
        return $hash;
    }

    return false;
}


function unique_id()
{
    $val = microtime();
    $val = md5($val);

    return substr($val, 4, 16);
}

function _hash_crypt_private($password, $setting, &$itoa64)
{
    $output = '*';

    // Check for correct hash
    if (substr($setting, 0, 3) != '$H$')
    {
        return $output;
    }

    $count_log2 = strpos($itoa64, $setting[3]);

    if ($count_log2 < 7 || $count_log2 > 30)
    {
        return $output;
    }

    $count = 1 << $count_log2;
    $salt = substr($setting, 4, 8);

    if (strlen($salt) != 8)
    {
        return $output;
    }

    /**
    * We're kind of forced to use MD5 here since it's the only
    * cryptographic primitive available in all versions of PHP
    * currently in use.  To implement our own low-level crypto
    * in PHP would result in much worse performance and
    * consequently in lower iteration counts and hashes that are
    * quicker to crack (by non-PHP code).
    */
    if (PHP_VERSION >= 5)
    {
        $hash = md5($salt . $password, true);
        do
        {
            $hash = md5($hash . $password, true);
        }
        while (--$count);
    }
    else
    {
        $hash = pack('H*', md5($salt . $password));
        do
        {
            $hash = pack('H*', md5($hash . $password));
        }
        while (--$count);
    }

    $output = substr($setting, 0, 12);
    $output .= $this->_hash_encode64($hash, 16, $itoa64);

    return $output;
}

function _hash_gensalt_private($input, &$itoa64, $iteration_count_log2 = 6)
{
    if ($iteration_count_log2 < 4 || $iteration_count_log2 > 31)
    {
        $iteration_count_log2 = 8;
    }

    $output = '$H$';
    $output .= $itoa64[min($iteration_count_log2 + ((PHP_VERSION >= 5) ? 5 : 3), 30)];
    $output .= $this->_hash_encode64($input, 6, $itoa64);

    return $output;
}

function _hash_encode64($input, $count, &$itoa64)
{
    $output = '';
    $i = 0;

    do
    {
        $value = ord($input[$i++]);
        $output .= $itoa64[$value & 0x3f];

        if ($i < $count)
        {
            $value |= ord($input[$i]) << 8;
        }

        $output .= $itoa64[($value >> 6) & 0x3f];

        if ($i++ >= $count)
        {
            break;
        }

        if ($i < $count)
        {
            $value |= ord($input[$i]) << 16;
        }

        $output .= $itoa64[($value >> 12) & 0x3f];

        if ($i++ >= $count)
        {
            break;
        }

        $output .= $itoa64[($value >> 18) & 0x3f];
    }
    while ($i < $count);

    return $output;
}
4

4 に答える 4

52

あなたが与えたコードはPHPASS、具体的には「移植可能な」アルゴリズムの移植です。の修飾に注意してくださいportable。これは、2 番目のコンストラクター パラメーターとしてphpass渡す場合にのみライブラリに適用されます。trueこれ以降、この回答では、ライブラリ自体ではなく、移植可能なアルゴリズムのみphpassを参照します。明示的に指定しない場合、ライブラリはデフォルトで bcrypt を実行します...portable

PHPBB チームはこれを自分で開発したのではなく (非常に良いことです)、phpass から直接移植しました (議論の余地があります)。

ここでいくつか質問する必要があります。

それは悪いですか?

短い答えはノーです。悪くはありません。それはかなり良いセキュリティを提供します。あなたが今これに関するコードを持っているなら、私は急いでそれをやめることはありません. ほとんどの用途に十分です。しかし、そうは言っても、新しいプロジェクトを開始する場合は、私が選択しないよりもはるかに優れた選択肢があります.

いくつかの弱点は何ですか?

  • Relative To pbkdf2:phpassアルゴリズムはhash()where pbkdf2()uses を使用しますhash_hmac()。現在、HMACはすべての呼び出しに対して内部的に 2 つのハッシュを実行しますが、PHP の実装では、1 回の呼び出しの約 1.6 倍しか実行しhash()ません (C は素晴らしいと思いませんか?)。したがって、2 つのハッシュを実行するhash_hmacのにかかる時間の 62% で2 つのハッシュを取得します。hash()

    どういう意味ですか?まあ、特定の runtimeでは、アルゴリズムよりpbkdf2も約 37.5%多いハッシュを実行します。所定の時間内により多くのハッシュ == 良い。より多くの計算が実行されるためです。phpass

    したがって、同じプリミティブを使用する場合よりもpbkdf2ほぼ37.5%強力です(この場合)。しかし、より強力なプリミティブを取ることもできます。したがって、 withを使用して、アルゴリズムよりも非常に大きな利点を得ることができます(主に、はより多くの計算を行う難しいアルゴリズムであるため)。phpassmd5pbkdf2pbkdf2sha512phpasssha512md5

    これはpbkdf2、同じ時間内により多くの計算を生成できるだけでなく、より難しい計算を生成できることを意味します。

    そうは言っても、違いはそれほど重要ではありません。それは非常に測定可能であり、pbkdf2間違いなく よりも「強い」phpass.

  • Relative To bcrypt: これを比較するのは非常に困難です。しかし、その表面を見てみましょう。、および PHP のループをphpass使用します。任意のプリミティブ (C) と PHP のループを使用します。カスタム アルゴリズムをすべて C で使用します (つまり、利用可能なハッシュとは異なるアルゴリズムです)。したがって、当然のことながら、アルゴリズムがすべてCであるという事実に大きな利点があります。これにより、単位時間あたりの「計算」が可能になります。これにより、より効率的な遅いアルゴリズムになります (特定のランタイムでより多くの計算が行われます)。md5pbkdf2bcryptbcrypt

    しかし、実行する計算の数と同じくらい重要なのは、計算の品質です。これは研究論文全体になる可能性がありますが、要するに、bcrypt が内部で使用する計算は、通常のハッシュ関数よりもはるかに実行が難しいという事実に帰着します。

    のより強力な性質の一例は、通常のハッシュ関数よりもはるかに大きな内部状態を使用するbcryptという事実です。512 ビットの内部状態を使用して、1024 ビットのブロックに対して計算します。代わりに、約 32kb の内部状態を使用して、576 ビットの単一ブロックに対して計算します。の内部状態が(andおよび)よりもはるかに大きいという事実は、 のより強い性質を部分的に説明しています。bcryptSHA512bcryptbcryptSHA512md5phpassbcrypt

避けるべきか

新しいプロジェクトの場合、絶対に. それが悪いというわけではありません。そうではありません。それは、明らかに強力なアルゴリズムが存在するということです (桁違いに)。では、なぜそれらを使用しないのですか?

bcryptがどのように強力であるかをさらに証明するには、パスワード ハッシュをクラックするために 25 GPU クラスターを起動したPassword13 のスライド (PDF)を確認してください。関連する結果は次のとおりです。

  • md5($password)
    • 1 秒あたり 1800 億回の推測
    • 9.4 時間 - 考えられるすべての 8 文字のパスワード
  • sha1($password)
    • 1 秒あたり 610 億回の推測
    • 27 時間 - 考えられるすべての 8 文字のパスワード
  • md5crypt(コストが 10 の phpass と 非常によく似ています):
    • 1 秒あたり 7700 万回の推測
    • 2.5 年 - 考えられるすべての 8 文字のパスワード
  • bcryptの費用で5
    • 1 秒あたり 71,000 回の推測
    • 2700 年 - 考えられるすべての 8 文字のパスワード

注:考えられる 8 文字のパスワードはすべて、94 文字セットを使用しています。

a-zA-Z0-9~`!@#$%^&*()_+-={}|[]\:";'<>,.?/

結論

したがって、新しいコードを作成する場合は、間違いなくbcrypt. 持っているphpasspbkdf2、現在運用中の場合は、アップグレードすることをお勧めしますが、「重大な脆弱性がある」という明確な理由はありません。

于 2013-04-16T18:04:30.017 に答える
1

私はbcryptに行きます.それは良いですレインボーテーブル攻撃から保護するためにソルトを組み込むことに加えて、bcryptは適応機能です.時間の経過とともに、反復カウントを増やして遅くすることができるため、ブルートフォース検索攻撃に対する耐性が維持されます.計算能力が上がっても。

実装: PHP でパスワードをハッシュするために bcrypt をどのように使用しますか?

于 2013-04-21T17:01:56.247 に答える
-5

(最初は私の英語でごめんなさい)

回答する前に、すべてのコメント投稿者が尋ねなければならない質問がいくつかあります。

そのコードはどこで使用されますか?

どのようなデータを保護しますか?

重要レベルのシステムに使用されますか?

わかりました、いくつかのユースケースがあります。私はアウト IS 用に簡略化されたパスワード ジェネレーターを使用しています。確かに「第 3 次世界大戦の準備ができている」わけではありませんが、ユーザーがパスワードを誰かに教えたり、マルウェアによってコンピューターから漏洩したりする可能性はまだはるかに大きいです。

  • md5 は弱いです。最新のフィンガープリント ジェネレーター (sha128、sha256、sha512) を使用してください。
  • 次のようなランダムな長さのソルトを使用します。

    private function generateSalt() {
      $salt = '';
      $i = rand(20, 40); //min,max length
      for($length = 0; $length < $i; $length++) {
        $salt .= chr(rand(33, 126));
      }        
      return $salt;
    }
    
  • 次に、パスワードを生成します。

    private function generatePass() {
      $vocabulary = 'abcdefghijklmnopqrstuvwxyz0123456789';
      $password = '';
      $i = rand(7,10);
      for($length = 0; $length < $i; $length++) {
          $password .= substr($vocabulary,rand(0, 35),1);
      }        
      return $password.'-'; // avoid copy`n`paste :)
    }
    
  • はい、パスワードはより強力にする必要がありますが、ユーザーは決して覚えておらず、ブラウザーに保存されるか、どこかに書き込まれます。期待とセキュリティ対現実。

    $newSalt = $this->generateSalt();
    $newPass = $this->generatePass();
    $newHash = hash('sha512', $newPass.':'.$newSalt); 
    // now store hash and salt into database
    
  • 新しいハッシュがありますが、まだ終わっていません。本当の作業が始まっています:

    1. ロギング
    2. セッションの保護
    3. user,user+ip,ip temp/perm 禁止

ログイン+ログアウト+パスワードの再作成/生成: 1つまたは2つのSQLテーブルと数KBのコード

セキュリティに関するその他の事項: 多くのテーブル、実際には数キロバイト以上のコード

于 2013-04-20T01:42:15.880 に答える