6

パスワードをハッシュ/暗号化してデータベースに保存する適切な方法について、いくつかの調査を行ってきました。私はSaltとHashingについて知っていたので、周りを見回したところ、PBKDF2が良い選択のようでした. それで、私はそれに関する良いチュートリアルと、PHP用のPBKDF2の適応を提供するこのWebサイトを見つけました(これは私が自分のWebサイトで使用しているものです)。

したがって、これらの関数を使用してパスワードを生成/作成するように Web サイトをセットアップしましたが、次のコードでわかるように:

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
    )); }

ソルトは create_hash 関数で生成され、最終的に sha256:1000:salt:hashed_pa​​ssword のようになる結果のハッシュに格納されます。これは、データベースに保存する必要があったものであり、結果のハッシュにソルトが含まれていたため、データベースに追加する必要はありませんでした。ただし、これを使用していくつかのテスト ユーザーを生成した後、データベース内のハッシュ化されたパスワード内に PBKDF2 設定を含めることが実際に良いことなのかどうか疑問に思いました。彼らは、私のデータベースをクラックした後、これらの一連の sha256:1000:salt:password のものを見て、各部分が何を表しているかを理解し、彼の試みに大いに役立ちます。 ?

そのため、PBKDF2 を介して実行する前に、生成してデータベースに保存する外部ソルトを持ち、パスワードにソルトを含めるように少し変更しました。次に、同じことを行って、指定されたパスワードをログイン用のデータベースにあるものと比較すると、機能します。私の唯一の懸念は、128 ビットのソルトを使用すると、結果として得られるパスワード ハッシュの長さがわずか 50 文字になることです。これは私には正しくないようです。

これが私の現在のコードです:

define("PBKDF2_HASH_ALGORITHM", "sha256");
define("PBKDF2_ITERATIONS", 10000);
define("PBKDF2_SALT_BYTES", 128);
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, $salt)
{
    // format: salthash
    return  
        base64_encode(pbkdf2(
            PBKDF2_HASH_ALGORITHM,
            $password,
            $salt,
            PBKDF2_ITERATIONS,
            PBKDF2_HASH_BYTES,
            true
        ));
}

function validate_password($password, $salt, $good_hash)
{
    $pbkdf2 = base64_decode($good_hash);
    return slow_equals(
        $pbkdf2,
        pbkdf2(
            PBKDF2_HASH_ALGORITHM,
            $password,
            $salt,
            PBKDF2_ITERATIONS,
            PBKDF2_HASH_BYTES,
            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; 
}

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));
}

パスワード保存のその形式に対する私の懸念は理にかなっていますか? それとも、とにかく同じことになるのでしょうか? データベースに私のソルトがあれば、それがクラックされた後でも、ハッカーはブルート フォースで通り抜けることができますか?

ありがとうございます。長い質問で申し訳ありません。

4

3 に答える 3

4

私の唯一の懸念は、128 ビットのソルトを使用すると、結果として得られるパスワード ハッシュの長さがわずか 50 文字になることです。これは私には正しくないようです。

結果のハッシュのサイズは、ソルトのサイズ、パスワード、および反復回数とはまったく関係ありません。最新の安全なハッシュ アルゴリズム (sha256 など) の出力は、入力に関係なく常に同じ長さです。長さゼロの入力は、25TB の入力と同じ長さの出力を持ちます。

それとも、とにかく同じことになるのでしょうか? データベースに私のソルトがあれば、それがクラックされた後でも、ハッカーはブルート フォースで通り抜けることができますか?

ソルトを 2 つの部分に分けることで、コードの複雑さが増します (一般的に悪いことです)。塩の欠片を保管する方法によっては、状況によっては少し利益が得られる場合があります。たとえば、静的なソルト フラグメントがデータベースの外部に格納されている場合、データベースのダンプは、データベース内のパスワード ハッシュに対してオフライン攻撃を実行するのに十分な情報を攻撃者に提供しません。

塩の破片が互いに別々に保管されている場合の利益は、少量の多層防御です. 複雑さのコストを上回るかどうかは判断次第ですが、XSS および SQL インジェクションの脆弱性 (攻撃者が上記のデータベース ダンプを取得する方法) を探し、セキュリティを確保するために時間を費やす方がよい可能性が高いと思います。SSL と証明書または強力なパスワードを使用して、システムのさまざまなコンポーネント間の接続を確立します。

于 2012-08-01T05:00:41.777 に答える
0

SHA256 の代わりに、bcrypt などの低速ハッシュ関数を使用する必要があります。パスワードをまったく保存しないでください (代わりに、openId などを使用します)。

そうは言っても、ユーザーごとに異なるソルトが必要です。それらを同じ行に、または(行っているように)同じフィールドに、またはまったく異なるデータベースに保存できます。どれだけの労力を費やすかは、実際にはあなたとあなたのパフォーマンス要件次第です。

各ユーザー/パスワードの個別のソルトに加えて、アプリケーションごとのソルトを検討することもできます。これはデータベースから除外されます。

于 2012-08-01T05:11:52.660 に答える