17

.NET System.Security.Cryptography クラスを使用してパスワードをハッシュしています。MD5、SHA1、SHA256、SHA384、SHA512 などのハッシュ用のアルゴリズムがいくつかあります。

結果のハッシュ値はバイト配列です。格納するために 16 進文字列に変換する必要がありますか、Convert.ToBase64String()、または他の何かに変換する必要がありますか? (Hex よりも短い Base64 を好みます)。

ちなみに、選択するハッシュアルゴリズムが非常に多いため、ランダムにSHA384を選択しましたが、「より良い」またはタスクに適したものはありますか?

コメントしてください。

最初の8つのコメントを読んだ後の更新:
回答とさらに読んだところ、MD5、SHA1は多かれ少なかれ同等のようです(SHA1の方が少し安全です)。SHA256、384、512 は、昇順でさらに優れたセキュリティを提供します。

私はフォートノックスを必要としないので(これは、URL、ブラウザ、インターネット、イントラネット、またはエクストラネットが見えない社内システム用です)、「塩漬け」ビジネスをバイパスします-誰かが盗むことができるかどうかを考えましたpasswords テーブルのほか、他のテーブルの実際のデータを盗む可能性もあります。

しかし、将来の参照のために「塩」の概念を保持します。ハッシュする前にソルトをパスワードの末尾に追加するか先頭に追加する必要があるかわかりませんが、違いはありますか? また、パスワードを保存するための余分なフィールドを避けるために、パスワード自体の最初の数文字をソルトとして使用することを考えていましたが、それは十分な長さではないと思います-ソルトは十分な長さでなければなりません。

コンセンサスは、base64 変換がストレージと比較のための合理的な選択であると言います。パスワードの最大長が 15 文字の場合、ハッシュ ストレージに必要なデータベース列の最大長を把握する必要があります。おそらくVarchar(64)?

皆様の貢献に感謝いたします。

4

10 に答える 10

12

解決策がフォートノックスでなくても、熱心にソルティングを実装する必要があります。多くの人が自分のパスワードを別の場所で再利用しており、攻撃者がクラックされたパスワード データベースを別の目的で使用することを選択した場合、侵入によって組織外にさらなる損害が生じるためです。

ソルティングは、辞書攻撃をより高価にします。使用する塩のサイズを決定することで、チャンスを微調整できます。Bruce Schneier の「Applied Cryptography」からの引用は次のとおりです。

「Salt は万能薬ではありません。Salt ビットの数を増やしてもすべてが解決するわけではありません。Salt は、パスワード ファイルに対する一般的な辞書攻撃から保護するだけであり、単一のパスワードに対する共同攻撃から保護することはできません。同じパスワードを持つ人々を保護します。複数のマシンを使用できますが、不適切なパスワードの選択が改善されるわけではありません。」

C# でのサンプルを次に示します。そんなに難しくない。また、使用するソルト サイズとハッシュ関数を選択できます。免責事項:パスワードの完全性が本当に気になる場合は、 bcryptなどを使用してください。


using System;
using System.IO;
using System.Reflection;
using System.Security.Cryptography;
using System.Text;

public class PassHash {
    private static readonly RandomNumberGenerator rng = RandomNumberGenerator.Create();
    public static readonly int DefaultSaltSize = 8; // 64-bit salt
    public readonly byte[] Salt;
    public readonly byte[] Passhash;

    internal PassHash(byte[] salt, byte[] passhash) {
        Salt = salt;
        Passhash = passhash;
    }

    public override String ToString() {
        return String.Format("{{'salt': '{0}', 'passhash': '{1}'}}",
                             Convert.ToBase64String(Salt),
                             Convert.ToBase64String(Passhash));
    }

    public static PassHash Encode<HA>(String password) where HA : HashAlgorithm {
        return Encode<HA>(password, DefaultSaltSize);
    }

    public static PassHash Encode<HA>(String password, int saltSize) where HA : HashAlgorithm {
        return Encode<HA>(password, GenerateSalt(saltSize));
    }

    private static PassHash Encode<HA>(string password, byte[] salt) where HA : HashAlgorithm {
        BindingFlags publicStatic = BindingFlags.Public | BindingFlags.Static;
        MethodInfo hasher_factory = typeof (HA).GetMethod("Create", publicStatic, Type.DefaultBinder, Type.EmptyTypes, null);
        using (HashAlgorithm hasher = (HashAlgorithm) hasher_factory.Invoke(null, null))
        {
            using (MemoryStream hashInput = new MemoryStream())
            {
                hashInput.Write(salt, 0, salt.Length);
                byte[] passwordBytes = Encoding.UTF8.GetBytes(password);
                hashInput.Write(passwordBytes, 0, passwordBytes.Length);
                hashInput.Seek(0, SeekOrigin.Begin);
                byte[] passhash = hasher.ComputeHash(hashInput);
                return new PassHash(salt, passhash);
            }
        }
    }

    private static byte[] GenerateSalt(int saltSize) {
        // This generates salt.
        // Rephrasing Schneier:
        // "salt" is a random string of bytes that is
        // combined with password bytes before being
        // operated by the one-way function.
        byte[] salt = new byte[saltSize];
        rng.GetBytes(salt);
        return salt;
    }

    public static bool Verify<HA>(string password, byte[] salt, byte[] passhash) where HA : HashAlgorithm {
        // OMG: I don't know how to compare byte arrays in C#.
        return Encode<HA>(password, salt).ToString() == new PassHash(salt, passhash).ToString();
    }
}

使用法:

新しいユーザーが資格情報を送信します。

PassHash ph = PassHash.Encode<SHA384>(new_user_password);

保存してどこかに...後で、ユーザーが再度ログインするときに、ソルトph.Saltph.Passhashパスハッシュを含むユーザーレコードを検索し、次のようにします。

PassHash.Verify<SHA384>(user_login_password, user_rec.salt, user_rec.passhash)

}

于 2009-06-05T00:06:35.027 に答える
8

Base64 は確かに 16 進数よりも短いですが、(関連するシンボルのために) URL になってしまった場合に注意する必要があるというわずかな欠点があります。基本的に、どこで使用しているかによって異なります。Hex は通常、肉眼で「読み取る」のが簡単であるという利点がありますが、これは本質的に意味のないデータになるため、ハッシュには関係ありません。(必要に応じて、デコードされたオンライン base64 をいつでも使用できます。)

もちろん、これはすべて、文字列が必要であることを前提としています。文字列は、不透明なバイト配列と比較して、保存、送信などが簡単で便利です。

于 2009-06-04T08:36:59.633 に答える
4

ちなみに、選択するハッシュアルゴリズムが非常に多いため、ランダムにSHA384を選択しましたが、「より良い」またはタスクに適したものはありますか?

一般に、MD5、SHA-0、または SHA-1 は避けます。しかし、現時点では SHA-384 で十分です。アルゴリズムに関する議論については、この質問を参照してください。

于 2009-06-04T08:41:28.877 に答える
4

以前に見た興味深いメモ: バイトの配列があり、それを base64 エンコードすると、結果の文字の一部が URL で問題を引き起こす可能性があります。削除され、base64 文字列を URL で使用できるようになります。二重にエンコードされた文字列を使用して比較できます。

ハッシュ アルゴリズムでは、おそらく sha384 で十分なアルゴリズムですが、必ずハッシュをソルトしてください。

于 2009-06-04T08:42:00.123 に答える
4

質問の 2 番目の部分に答えるには、SHA384 はおそらくやり過ぎです。SHA1 はあなたの目的をうまく果たしますが、それには理論的な悪用があります (同じ値にハッシュされる改ざんされたドキュメントを作成できることに関連して、心配する必要はありません)。

また、そうでない場合は、パスワードをハッシュする前に必ずソルトして、より一般的なハッカーの攻撃 (類似のパスワードやレインボー テーブルの識別など) から保護してください。詳細については、この記事をお読みください。

記録として、.NET には Base64 との間で変換するための組み込みルーチンがあるため、私は Base64 が好きです。

于 2009-06-04T08:50:28.997 に答える
3

プレーンバイトに変換することもできます。したがって、128 ビットの MD5 ハッシュ値は、128 ビット / 8 ビット/バイト = 16 バイトの長さの文字列になります。

于 2009-06-04T08:36:26.030 に答える
0

私はBase64を使用します....ハッシュしているパスワードの場合は、ハッシュをソルトすることを忘れないでください:)パスワードについて話す場合、SHA384より小さいものはセキュリティリスクです。これは、高品質のレインボーテーブルを簡単に取得できるためです。 SHA1 およびその他の一般的なハッシュ アルゴリズム。

于 2009-06-04T09:57:41.203 に答える
0

答えは、コンテキストと制約によって異なります。Base64 はよりコンパクトになりますが、16 進エンコーディングと比較して、生成するための計算コストが少し高くなります。

ハッシュされたデータにソルトを追加する必要があります。これは、ハッシュするパスワードの前に置かれるランダムな値です。次に、ソルト値をハッシュ値とともに保存して、ハッシュ値を再生成できるようにします。

このソルトの役割は、一般的なパスワードをハッシュ値にマッピングする辞書の作成を防ぐことです。誰かがハッシュ化されたパスワード ファイルのコピーを入手できれば、この辞書を使用して平文のパスワードを見つけることができます。16 ビットのソルトを使用すると、同じ平文パスワードに対して 65536 の異なるハッシュ値が得られます。辞書攻撃の効率が低下します。パスワードをハッシュして検証する手間がかからないため、64 ビットのソルトを選択することをお勧めします。

また、ハッシュ値にマジック バイト シーケンス (定数バイト シーケンス) を追加する必要があります。攻撃者は、適切なハッシュ値を生成できるようにするために、このマジック バイト シーケンスを知る必要があります。したがって、ハッシュ化されたパスワード ファイルしかない場合、彼はそれほど遠くまでは行かないでしょう。彼は、コードのどこかに埋もれている可能性が最も高い魔法の言葉を取得する必要があります。

SHA1 を使用するアルゴリズムに関しては非常に優れており、SHA256 はベルトとストラップになります。MD5 は計算コストがはるかに低く、ほとんどのユース ケースを満たします。処理およびストレージ リソースが制限要因になる可能性がある場合は、これを考慮してください。

于 2009-06-04T08:57:22.260 に答える
0

パスワードについては、どれも完璧に機能すると思います。SHA 512 はより高いキーを作成し、「衝突」の可能性を減らし、異なるパスワードが同じハッシュを持つ可能性があることを認識していますが、可能性は非常に低いです。

于 2009-06-04T08:42:39.967 に答える
0

アルファベットと数字だけを含む Base36 または Base62 表記を使用するのはどうでしょうか (URL で安全に送信できます)。Javaで次のことを試しただけです

BigInteger hexNumber = new BigInteger(hexString, 16);
// Convert it to Base 36 yields smaller string than hexString
hexNumber.toString(36);

Base36 数値を 16 進数に戻す

BigInteger b36String= new BigInteger(b36String, 36);
// Convert it to Base 16 yields original hex string
hexNumber.toString(16);

ところで、Java の BigInteger の最大基数値は 36 です。

Base62 の場合、62 文字 (下位 26 桁、上位 26 桁、10 桁) のルックアップ テーブルを作成し、数学的に 16 進数を 64 で割り、余りを使用してルックアップ テーブルから取得した文字を追加し続けることができます。

提案は大歓迎です。

于 2013-11-19T12:56:29.447 に答える