web-dev-qa-db-ja.com

データベースに安全に保存できる文字列の.net(c#)でHashCodeを作成するにはどうすればよいですか?

Eric Lippertによる GetHashCodeのガイドラインとルール から引用するには:

ルール:GetHashCodeのコンシューマーは、時間の経過やアプリドメイン全体での安定性に依存することはできません

Name、Addressなどの一連のフィールドを持つCustomerオブジェクトがあるとします。 2つの異なるプロセスでまったく同じデータを持つ2つのオブジェクトを作成する場合、それらは同じハッシュコードを返す必要はありません。 1つのプロセスで火曜日にそのようなオブジェクトを作成し、それをシャットダウンして、水曜日にプログラムを再度実行すると、ハッシュコードが異なる場合があります。

これは過去に人々を噛んだ。 System.String.GetHashCodeのドキュメントでは、2つの同一の文字列がCLRの異なるバージョンで異なるハッシュコードを持つ可能性があり、実際にはそうであると明記しています。 文字列ハッシュをデータベースに保存しないでください。文字列ハッシュは永久に同じになるとは限りません。

では、データベースに格納できる文字列のHashCodeを作成する正しい方法は何でしょうか。

(私がこのバグを私が書いたソフトウェアに残した最初の人物ではないことを教えてください!)

45
Ian Ringrose

ハッシュにどのプロパティを設定するかによって異なります。たとえば、あなたはcouldのように書くだけです:

_public int HashString(string text)
{
    // TODO: Determine nullity policy.

    unchecked
    {
        int hash = 23;
        foreach (char c in text)
        {
            hash = hash * 31 + c;
        }
        return hash;
    }
}
_

あなたがdocumentである限り、それはハッシュが計算される方法であり、それは有効です。それは決して暗号的に安全なものではありませんが、問題なく持続できます。順序の意味で完全に等しい2つの文字列(つまり、文化的平等などが適用されず、文字ごとにまったく同じ)は、このコードで同じハッシュを生成します。

非文書化ハッシュに依存するときに問題が発生します。つまり、GetHashCode()に従いますが、バージョン間で同じであることが保証されていません... string.GetHashCode()

このように独自のハッシュを記述して文書化することは、「この機密情報はMD5(またはその他)でハッシュ化される」と言ったようなものです。明確に定義されたハッシュである限り、それで問題ありません。

編集:他の回答では、SHA-1やMD5などの暗号化ハッシュの使用が提案されています。安定性だけでなく暗号化セキュリティの要件があることがわかるまでは、文字列をバイト配列に変換してハッシュするという厳格な規則を通過しても意味がありません。もちろん、ハッシュisがセキュリティ関連のあらゆるものに使用されることを意図している場合、業界標準のハッシュは正確に到達すべきものです。しかし、それは質問のどこにも言及されていませんでした。

74
Jon Skeet

これは 現在の方法の再実装です。NETは64ビットシステムの文字列ハッシュコードを計算します 。これは実際のGetHashCode()のようにポインターを使用しないため、少し遅くなりますが、stringへの内部変更に対する耐性が高くなり、より均一に分散されたハッシュコードが得られます Jon Skeetのバージョン よりも、辞書での検索時間が改善される可能性があります。

public static class StringExtensionMethods
{
    public static int GetStableHashCode(this string str)
    {
        unchecked
        {
            int hash1 = 5381;
            int hash2 = hash1;

            for(int i = 0; i < str.Length && str[i] != '\0'; i += 2)
            {
                hash1 = ((hash1 << 5) + hash1) ^ str[i];
                if (i == str.Length - 1 || str[i+1] == '\0')
                    break;
                hash2 = ((hash2 << 5) + hash2) ^ str[i+1];
            }

            return hash1 + (hash2*1566083941);
        }
    }
}
10

答えは、独自のハッシュ関数を作成することです。あなたが投稿した記事へのコメントのリンクをたどることでいくつかのソースを見つけることができます。または、もともと暗号化(MD5、SHA1など)を目的とした組み込みハッシュ関数を使用して、すべてのビットを使用することはできません。

1
Gabe