如何在.net(c#)中为一个可以安全存储在数据库中的字符串创建一个HashCode?

qhhrdooz  于 2023-05-08  发布在  .NET
关注(0)|答案(4)|浏览(236)

引用Eric Lippert的Guidelines and rules for GetHashCode
规则:GetHashCode的消费者不能依赖于它在一段时间内或跨应用领域的稳定性
假设您有一个Customer对象,它有一堆字段,如Name、Address等。如果在两个不同的进程中使用完全相同的数据创建两个这样的对象,则它们不必返回相同的哈希代码。如果您在星期二在一个进程中创建这样一个对象,然后关闭它,并在星期三再次运行该程序,则哈希代码可能会有所不同。
这在过去曾咬伤过人。System.String.GetHashCode的文档特别指出,在不同版本的CLR中,两个相同的字符串可以有不同的哈希码,事实上也是如此。不要在数据库中存储字符串哈希,并期望它们永远相同,因为它们不会。
那么,创建一个可以存储在数据库中的字符串的HashCode的正确方法是什么呢?
(请告诉我,我不是第一个在我写的软件中留下这个bug的人!)

ffvjumwh

ffvjumwh1#

这取决于您希望该哈希具有什么属性。例如,你可以**写这样的东西:

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

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

只要你 * 证明 * 这就是哈希的计算方式,那就是有效的。它在密码学上是不安全的,但你可以坚持下去,没有任何问题。在序数意义上绝对相等的两个字符串(即在没有应用文化平等等的情况下,完全逐字符相同)将使用该代码产生相同的散列。
当你依赖于 * 无文档的 * 散列时,问题就来了--即。一些服从GetHashCode()的东西,但不能保证从一个版本到另一个版本保持相同。比如string.GetHashCode()
像这样编写和记录您自己的散列有点像说,“这个敏感信息是用MD5(或其他)散列的”。只要它是一个定义良好的散列,就可以了。
编辑:其他答案建议使用加密哈希,如SHA-1或MD5。我要说的是,除非我们知道需要加密安全性而不仅仅是稳定性,否则将字符串转换为字节数组并对其进行散列的繁琐过程是没有意义的。当然,如果哈希 * 是 * 用于任何与安全相关的事情,那么行业标准的哈希 * 正是 * 你应该达到的。但问题里没有提到这点。

xmq68pz9

xmq68pz92#

这里是.NET计算64位系统的字符串哈希代码的当前方法的重新实现。这不像真实的的GetHashCode()那样使用指针,所以它会稍微慢一点,但它确实使它对string的内部更改更具弹性,这将给予比Jon Skeet's version更均匀分布的哈希码,这可能会导致字典中更好的查找时间。

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

v2g6jxz63#

现在有一个System.IO.Hashing包,它提供了稳定和标准化的非加密哈希算法。虽然它们是为字节序列设计的,但通过Span安全且高效地使用它们是相当简单的:

var input = "Hello world";
var inputBytes = MemoryMarshal.AsBytes(input.AsSpan());
var hash = System.IO.Hashing.XxHash32.HashToUInt32(inputBytes);
Console.WriteLine(hash); // 899079058

但是请注意,由于将字符重新解释为字节,系统的字节顺序会影响结果,因此如果您移动到big-endian系统,上面的哈希将有所不同。如果这是一个问题,您可以检查BitConverter.IsLittleEndian,如果是false,则交换字节。

zour9fqk

zour9fqk4#

答案是编写自己的哈希函数。你可以通过你发表的文章的评论中的链接找到一些来源。或者你可以使用一个内置的哈希函数,它最初是用于加密的(MD5,SHA1等),只是不使用所有的位。

相关问题