0

いわゆる「レインボーテーブル」を使用したリバースエンジニアリングの対象となる可能性がある、「ソルト化されていない」パスワードハッシュをデータベースに保存することの危険性について説明しているこの記事に出くわしました。

また、この C# コード サンプルも付属しています。このC# コード サンプルでは、​​ユーザー パスワード データベース テーブルに 2 つのハッシュ列を格納する必要があります (従来の 1 つではなく)。ハッシュ、および新しい列を追加するには、データベースの再構築が必要になります。そのため、それを行う前に、別の代替案を探していましたが、ここに思いつきました。

パスワードの SHA1 ハッシュを単純に計算する代わりに、疑似乱数 (ただし一貫性のある) データの長いシーケンスをパディングしてからハッシュを計算する関数を次に示します。

byte[] computeSecureHash(string strUserPassword)
{
    //RETURN: = SHA1 byte array on the 'strUserPassword'

    //Make simple junk array based on the password
    ushort v = 117;
    byte[] arrJunk = new byte[24];
    for (int c = 0, i = 0; i < arrJunk.Length; i++)
    {
        v ^= strUserPassword[c++];
        v *= 7;
        arrJunk[i] = (byte)v;

        if (c >= strUserPassword.Length)
            c = 0;
    }

    //Make crypto byte array based on the password
    Rfc2898DeriveBytes pbkdf2 = new Rfc2898DeriveBytes(strUserPassword, arrJunk);
    pbkdf2.IterationCount = 1000;
    byte[] arrCrypto = pbkdf2.GetBytes(128);

    //Pad actual password
    string strUserPassword_Padded = "";

    const string strChars2Use = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ`-=[]\\;',./~!@#$%^&*()_+{}|:\"<>?";
    int nHalfArrCrypto = arrCrypto.Length / 2;

    //Left side
    for (int i = 0; i < nHalfArrCrypto; i++)
    {
        strUserPassword_Padded += strChars2Use[arrCrypto[i] % strChars2Use.Length];
    }

    strUserPassword_Padded += strUserPassword;

    //Right side
    for (int i = nHalfArrCrypto; i < arrCrypto.Length; i++)
    {
        strUserPassword_Padded += strChars2Use[arrCrypto[i] % strChars2Use.Length];
    }

    //For user's password "123"
    //the 'strUserPassword_Padded' becomes:
    //"bwDR]_B>H5t-k:eIq?r_wGBWqWfs#tcAE~DQ5?(Pbj#<+Cw:9(r!B[f_.S<pCjn-123b9l3<Sz^D~>G}v)?NuHT4BZ-pI2$W[kW1e4KO\"`rTg3H`}&jmtrFh1J5c72:})tQ"

    //And now compuse SHA1 on the padded password
    SHA1 sha1 = new SHA1CryptoServiceProvider();
    byte[] bytesInputData = System.Text.Encoding.UTF8.GetBytes(strUserPassword_Padded);
    return sha1.ComputeHash(bytesInputData);
}

だから私の質問は、誰かがこのコードを見直して、この方法でそれを行うことの危険性と、作者が彼のコードで提案したことの危険性を教えてもらえますか? 私のコード サンプルの場合、データベースには 2 つのハッシュ (パスワード ハッシュ + ソルト ハッシュ) ではなく 1 つのハッシュのみを格納する必要があります。

4

5 に答える 5

6

パスワードからのみ派生したソルトはほとんど無意味です。全体的にわずかに異なる (しかし一定の) ハッシュ関数を作成しただけです。単一のレインボー テーブル (カスタム テーブルではありますが) を使用して、データベース全体をターゲットにすることができます。

さらに、ソルトがパスワードから派生した場合、同一のパスワードは同一の「パスワード ハッシュ」として表示されます。簡単なパスワードは重複して表示される可能性があります。つまり、独自のレインボー テーブルを生成していることになります。

各パスワードに対して個別に生成された一意のソルトを保存することの要点は、すべてのパスワードが一意のハッシュ関数でハッシュされるようにすることです。したがって、データベース全体で使用できる単一のレインボー テーブルはありません。

于 2013-03-20T00:35:20.013 に答える
3

これにより、問題が部分的に軽減されるだけです。あなたが行ったことは、基本的にキー付きハッシュ関数を作成したことです。

このような関数を使用すると、一般的なレインボー テーブルは適用できなくなりますが、攻撃者がデータベース全体を入手した場合、依然として危険にさらされます。この場合、彼はこのランダムな文字列に基づいて新しいレインボー テーブルを作成できます。この新しいテーブルを使用すると、システム内の少なくとも 1 つのアカウントに侵入する可能性が高くなります。

個別のソルトを追加することは、パスワードごとに異なるハッシュ関数を使用することと同じです。したがって、可能なソルトごとに個別のレインボー テーブルが必要になり、攻撃が非常に高価になります。攻撃者が 1 つのソルトに対してレインボー テーブルを作成した場合、そのソルトのパスワードのみを破ることができます。

また、ランダム性が一定のままであれば、どれだけ「静的」なランダム性を追加しても問題ないことを指摘したいと思います。

于 2013-03-20T00:41:44.760 に答える
2

ジャンクはパスワードから派生しているため、ソルティング効果はありません。テーブル全体に適用できるレインボー テーブルを生成できます。

ただし、1 つの列だけを使用する場合は、答えはより簡単です。

pbkdf2を使用してハッシュを直接作成し、64 ビット (8 バイト) のランダムソルトを作成し、より高い反復カウント (4000-10000) を使用します。列が 160 ビット (20 バイト) しか保持できない場合は、12 バイトを生成し(列がさらに保持できる場合は 24 バイトまで)、列にsalt + hash concatenatedを格納します。

したがって、比較するときは、ソルトを取得するために最初の 8 バイトを読み取るだけです。

于 2013-03-20T13:40:17.020 に答える
0

いつでも現在のデータベースのハッシュ化されたパスワードを取得し、元のパスワードであるかのように、別のハッシュ アルゴリズム + ソルトを使用して再度ハッシュ化できます。ハッシングの上にハッシングを重ねることは完全に安全であり、実際、多くの場合、この方法の方が安全です。ただし、プロセスを元に戻すこともできる必要があることに注意してください。そうしないと、物事が壊れます。

于 2013-03-20T00:43:37.900 に答える
0

ソルトフィールドを追加するだけのデータベースの再構築がより良いオプションです(または、適切に行う場合は実際に唯一のオプションですが、他の人が投稿したように、カラントハッシュフィールドを使用してソルトを保存できます)

彼らが次に有効なユーザー名とパスワードでログインするとき (これは、パスワード ハッシュを変更する必要がある場合、またはすべてのパスワードを強制的に変更する場合のみです) ソルト データがない場合は、大きなランダム ソルトを生成し、それを保存して再生成します。パスワード+ソルトからのハッシュ(理想的にはSHA1よりも優れたものを使用し、pbkdf2は10,000よりも高く設定する必要がありますが、サーバーリソースによって異なります)

于 2013-03-21T05:31:30.117 に答える