Java 哈希映射键不是随机的

Java 哈希映射键不是随机的,java,security,hash,collections,hashmap,Java,Security,Hash,Collections,Hashmap,Java的HashMap实现似乎总是将键放在相同的容器中(至少我看到了整数键)。也就是说,散列是确定性的,在所有运行中,它产生相同的值。 我听说有些语言会随机插入密钥,因此出于安全原因,密钥将存储在哪个存储桶中是不可预测的。 为什么Java的密钥总是一样的?在Java中,单个类负责实现一个默认的hashCode()方法(或从对象类继承一个),而复杂的安全场景(如击败高级DoS攻击)不是对象,整数等类的责任,等等 因此,大多数类使用一个非常简单、快速的实现,试图确保在常见情况下公平地分布 如果您觉

Java的HashMap实现似乎总是将键放在相同的容器中(至少我看到了整数键)。也就是说,散列是确定性的,在所有运行中,它产生相同的值。
我听说有些语言会随机插入密钥,因此出于安全原因,密钥将存储在哪个存储桶中是不可预测的。

为什么Java的密钥总是一样的?

在Java中,单个类负责实现一个默认的hashCode()方法(或从
对象
类继承一个),而复杂的安全场景(如击败高级DoS攻击)不是
对象
整数
等类的责任,等等

因此,大多数类使用一个非常简单、快速的实现,试图确保在常见情况下公平地分布


如果您觉得实现自定义哈希策略很重要,无论是因为您想避免黑客攻击,还是因为您知道您的特定用法可能会与默认方法产生大量冲突,您都可以使用类似于
THashMap
,的集合,它允许您提供特定于集合实例的自定义哈希策略。

这里关注的攻击是拒绝服务(DoS)。对手选择一组击中同一桶的钥匙。这将映射操作的性能从O(1)转换为O(n)。这样做n次(比如构造地图),我们从O(n)到O(n^2)。还有定时攻击的可能性,但我很方便地忽略了这一点

一般来说,大多数库代码都会假定不需要任何操作来避免DoS。然而,最近一些Java实现已经使用杂音散列来随机化
String
的散列函数,以避免某些攻击。杂音将每个进程的随机数混合到哈希代码的生成中,这样函数对于进程来说是稳定的,但从外部很难(尽管不一定不可能)理解。最近,如果冲突过多,并且键适当地实现了
Comparable
,则这一点被替换为返回到树结构


如果您在找到代码的情况下担心此类攻击,您可以使用其他
Map
实现,例如
java.util.TreeMap

对于java 7来说,情况并非如此,它为每个HashMap实例添加了唯一的哈希种子。页面上有更多信息


为了提高性能,Java8中删除了该机制,取而代之的是将可比较的键(如字符串)转换为平衡树以避免DoS安全问题。页面上有更多信息。

要完成其他答案,我要提到当
HashMap
的键使用内置的
Object.hashCode
,I。E不要重写这个方法,这是一个非常常见的情况,对象的哈希代码是使用随机数生成器计算的,这使得整个系统的行为不太确定。查看此问题:了解更多详细信息。

可能是因为HashMap没有针对安全性进行优化?(FWIW,我对bucket随机化如何提高安全性很感兴趣。)@OliverCharlesworth:我不知道任何web应用程序都会使用另一种hashmap实现java(除了性能原因之外,还有一些不同)@OliverCharlesworth:那么所有的hashmap不是都应该随机化吗?@OliverCharlesworth它可以防止某些类别的拒绝服务攻击;如果攻击者可以给你所有散列到同一个bucket的密钥,那么他们可以使你的代码运行缓慢。我不会为此使用
HashMap
,它不是为此目的创建的。。。?为什么不随机创建关键点?由于您的安全原因,哈希将是可以的……哈希代码对于一个对象总是返回相同的数字,这是必须的。实现如何决定如何使用哈希代码,使其不会落入同一个桶中,这是哈希表实现的问题,而不是object@Jim:同意。hashCode还应该做出合理的努力,以避免不同的值也会产生相同的hash值,或者甚至可能落入相同存储桶的不同hash值。但在某种程度上,我们需要认识到,每个单独的类都不能负责生成一个绝对正确、不可用的哈希函数。Trove肯定不是一个单独的项目,它现在应该被称为“Java自定义数据结构”。看见从任何角度来看,本文中提到的所有项目都比Trove好。(它们都允许提供定制的散列策略。)Trove项目的名称也导致了一个错误,即这是GNU项目。事实上,它只是使用(L?)GPL许可证,这是连接项目和GNU组织的唯一东西。@leventov:谢谢您的反馈。我从来没有理由使用这些。Trove只是第一个出现在我的谷歌搜索中。我不明白
TreeMap
如何更适合这种情况?对于商业级应用程序来说,这种漏洞也是微不足道的吗?@Jim:TreeMap已经保证了O(log(n))性能,无论提供什么输入,所以它不能被强制进入O(n)只需提供一组特定的最坏情况值即可提高性能。
TreeMap
将始终在
O(log n)
时间内执行常见的
Map
操作,因为如果选择的键试图强制一个不稳定的结构,它将重新平衡。在正常的操作中,这并没有那么快,但在最坏的情况下要好得多。也就是说,对于当前版本的Java,
HashMap
将退回到一个平衡的二进制搜索树,如果单个bucket中有太多冲突(并且如果键是可比较的)。因此,现在可能没有必要使用
TreeMap
。这是“为什么…”问题的正确答案