哈希表的潜在安全缺陷:为什么jdk8突然修改了哈希表结构

1. 导语

背过java面经或者研读过jdk源码的同学都会知道:相对于jdk7中的hash表,java8以后的hash表中相同键值元素超过8个时,会自动将hash表的底层实现链表转成平衡树,而jdk7得hash表底层则只使用链表进行实现,那么,为什么jdk会突然修改hash表的底层实现呢?究其原因在于,2011年时,产生了一种专门针对于hash表结构的攻击方案------哈希洪水攻击。本文将对于哈希洪水攻击(Hash-Flooding Attack)进行讲解,并说明jdk中hash表底层使用混合数据结构实现所带来的好处。

2. 什么是哈希洪水攻击?

哈希洪水攻击(Hash-Flooding Attack) 是一种 拒绝服务攻击(Denial of Service) ,一旦后端接口存在合适的攻击面,攻击者就能通过哈希洪水攻击轻松让整台服务器陷入瘫痪。

3. 哈希洪水攻击的基本原理

那么哈希洪水攻击的基本原理是什么呢?这个需要从数据结构谈起

在各种常用的数据结构里,有些数据结构的"平均运行时间"和"最差运行时间"会差很远,比如哈希表(Hash Table)。假设我们想要连续插入 <math xmlns="http://www.w3.org/1998/Math/MathML"> N N </math>N个元素到哈希表中:

  • 如果这些元素的键(Key)极少出现相同哈希值,这项任务就只需 <math xmlns="http://www.w3.org/1998/Math/MathML"> O ( N ) O(N) </math>O(N)的时间。
  • 如果这些键频繁出现相同的哈希值(频繁发生碰撞 ),这项任务就需要 <math xmlns="http://www.w3.org/1998/Math/MathML"> O ( N 2 ) O(N^2) </math>O(N2)的时间。

2003年,Scott A. Crosby 和 Dan S. Wallach 两位研究人员发表了一篇论文:Denial of Service via Algorithmic Complexity Attacks。在这篇论文里他们首次提出:既然有些数据结构的最差运行时间极高,那么我们完全可以利用算法上的漏洞,强行构造出一个最差情况,让服务器把全部的资源都浪费在处理这个最差情况上,从而无法为正常的用户提供服务。从而进行拒绝服务攻击。

3.1 哈希表最差时间复杂度的说明

假定有一个空的单链表哈希表,我们向其中连续插入 <math xmlns="http://www.w3.org/1998/Math/MathML"> n n </math>n个元素。这 <math xmlns="http://www.w3.org/1998/Math/MathML"> n n </math>n个元素的键两两均不相同,但是全部有相同的哈希值,也就是说会被插入到同一个位置上去。

每一次插入时,都需要两个步骤:首先遍历整条单链表,确认现在这个键确实不在表中。确认不存在的话,就将新的元素追加到单链表结尾。

这样一来,插入第1个元素需要0+1个常数时间操作;插入第2个元素需要1+1个常数时间操作;......;插入第n个元素时需要n-1+1个常数时间操作。最后总共需要
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> 1 + 2 + ... + n = 0.5 n ( n + 1 ) = O ( n 2 ) 1+2+...+n=0.5n(n+1)=O(n^2) </math>1+2+...+n=0.5n(n+1)=O(n2)

个常数时间操作,才能把 <math xmlns="http://www.w3.org/1998/Math/MathML"> n n </math>n个元素插入到哈希表中。

3.2 攻击方案

以老版本的 java.lang.String.hashCode()为例,该函数计算一个字符串的哈希值的方法为:
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> h a s h ( s ) = s [ 0 ] ∗ 3 1 n − 1 + s [ 1 ] ∗ 3 1 n − 2 + . . . + s [ n − 1 ] hash(s) = s[0]*31^{n-1} + s[1]*31^{n - 2} + ... + s[n - 1] </math>hash(s)=s[0]∗31n−1+s[1]∗31n−2+...+s[n−1]

由此,可以构建出能够构建出大量具有相同哈希值的字符串,然后将其插入到哈希表中,就会导致系统必须去处理一个 <math xmlns="http://www.w3.org/1998/Math/MathML"> O ( n 2 ) O(n^2) </math>O(n2)的算法。

4. 针对哈希洪水攻击的防御方案

4.1 带密钥哈希算法

能够构建大量具有相同哈希值的输入的前提,是攻击者能够白盒的掌握系统中所使用的哈希算法的全部细节,由此,就产生了带密钥的哈希算法,即: 每建立一张哈希表时,就产生一个随机种子,这个随机种子是私有的,每当计算哈希值时,对于要计算的值上加上这个随机种子后再进行计算,从而防止攻击方生成大量相同哈希值的输入。

4.2 JAVA的解决方案

从JDK 8开始,HashMap、LinkedHashMap和ConcurrentHashMap三个类引入了一套新的策略来处理哈希碰撞。

  • 当一个位置存储的元素个数小于8个时,仍然使用链表存储。
  • 当一个位置存储的元素个数大于等于8个时,改为使用平衡树来存储。

这样一来,就能保证最差的运行时间是 <math xmlns="http://www.w3.org/1998/Math/MathML"> O ( n l o g ⁡ n ) O(nlog⁡n) </math>O(nlog⁡n) 了。

相关推荐
2302_813806224 小时前
【嵌入式修炼:数据结构篇】——数据结构总结
数据结构
Wei&Yan5 小时前
数据结构——顺序表(静/动态代码实现)
数据结构·c++·算法·visual studio code
devmoon5 小时前
在 Polkadot 上部署独立区块链Paseo 测试网实战部署指南
开发语言·安全·区块链·polkadot·erc-20·测试网·独立链
成茂峰5 小时前
软考高级·系统架构设计师 | 四、信息技术安全知识
安全·信息安全·系统架构·架构设计师
向哆哆5 小时前
CANN生态安全保障:cann-security-module技术解读
人工智能·安全·cann
long3165 小时前
Aho-Corasick 模式搜索算法
java·数据结构·spring boot·后端·算法·排序算法
wuli_滔滔6 小时前
CANN安全机制源码探秘 仓库中的权限校验与数据加密实现
安全·cann
liann1196 小时前
3.1_网络——基础
网络·安全·web安全·http·网络安全
独行soc6 小时前
2026年渗透测试面试题总结-17(题目+回答)
android·网络·安全·web安全·渗透测试·安全狮
小羊不会打字7 小时前
CANN 生态中的模型安全加固:`secure-model-deploy` 项目实践指南
安全·neo4j