如何在.net(c#)中创build一个可安全存储在数据库中的string的HashCode?
引用Eric Lippert的GetHashCode指南和规则 :
规则: GetHashCode的使用者不能依赖于它随着时间的推移或跨越应用程序域的稳定性
假设你有一个Customer对象,有一堆如Name,Address等字段。 如果在两个不同的进程中使两个这样的对象具有完全相同的数据,则不必返回相同的哈希码。 如果星期二在一个进程中创build这样的对象,closures它,并在周三再次运行程序,哈希代码可以不同。
这已经让人们过去了。 System.String.GetHashCode的文档特别指出,两个相同的string可以在CLR的不同版本中具有不同的哈希码,事实上他们也可以。 不要在数据库中存储string散列,并期望它们永远是一样的,因为它们不会。
那么创build一个可以存储在数据库中的string的HashCode的正确方法是什么?
(请告诉我,我不是第一个在我写的软件中留下这个bug的人!)
这取决于你想要散列的属性。 例如,你可以写这样的东西:
public int HashString(string text) { // TODO: Determine nullity policy. unchecked { int hash = 23; foreach (char c in text) { hash = hash * 31 + c; } return hash; } }
只要你logging了散列的计算方式,这是有效的。 这绝不是密码保护或任何类似的东西,但你可以坚持下去,没有任何问题。 在序数意义上两个绝对相等的string(即,没有应用文化等同性,恰好字符相同)将产生与该代码相同的散列。
当你依赖无证散列时,问题就来了 – 也就是说服从GetHashCode()
东西,但是不能保证从版本到版本保持不变…比如string.GetHashCode()
。
像这样写和logging你自己的散列有点像说:“这个敏感信息被MD5散列(或其他)”。 只要它是一个明确的散列,那就好了。
编辑:其他答案build议使用encryption哈希,如SHA-1或MD5。 我会说,直到我们知道需要密码安全性,而不仅仅是稳定性,通过将string转换为字节数组和哈希来完成整个过程是毫无意义的。 当然,如果散列意味着用于任何与安全有关的事情,那么行业标准的散列正是您应该达到的。 但这个问题在任何地方都没有提到。
例如,您可以创build一个MD5散列。
这里是一个重新实现当前的方式.NET计算64位系统的string哈希码 。 这不会像真正的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); } } }
答案是只写自己的散列函数。 您可以通过在您发布的文章的评论链接中find某些源代码。 或者,您可以使用最初用于encryption(MD5,SHA1等)的内置散列函数,而不是使用所有的位。