Python中整数哈希映射的哈希洪水攻击

1 投票
1 回答
37 浏览
提问于 2025-04-14 16:04

为什么Python还没有修复整数哈希表的哈希洪水攻击问题呢?举个例子,我可以创建一个很长的数字列表,让每个数字和前一个数字发生冲突,这样就会导致哈希表的碰撞,结果是时间复杂度变成了o(n**2)。而对于字符串来说就没这个问题,因为Python使用了“SipHash”算法,这种算法设计得能抵抗哈希洪水攻击。

看看这段代码

import time
m = 2**61 - 1  
n = 10000  
from collections import Counter

collide = [m * i for i in range(1, n + 1)]
s = time.time()
c = Counter(collide)
e = time.time()
print(e-s)

another = [k for k in range(n)]
s = time.time()
c = Counter(another)
e = time.time()
print(e-s)

在这个例子中,碰撞数组的处理时间是1.4420311450958252秒,而正常数组的处理时间只有0.0005929470062255859秒。有没有什么办法可以缓解这个问题,因为Python不允许自定义哈希实现?

1 个回答

0

好吧,如果你确定自己处理的整数大多属于特殊情况,那么你可以考虑在把它们放进字典之前先进行一些处理。

  • 转换成字符串 - 在我的测试中,这个简单的办法能让你速度提升很多。
import time
m = 2**61 - 1
n = 10000
from collections import Counter

collide = [str(m * i) for i in range(1, n + 1)]
s = time.time()
c = Counter(collide)
e = time.time()
print(e-s)

another = [k for k in range(n)]
s = time.time()
c = Counter(another)
e = time.time()
print(e-s)

结果是:

0.0013701915740966797
0.0009481906890869141
  • 对整数进行一些简单的变换,然后记得在使用之前再变回来。例如,由于Python的整数哈希函数希望低位有变化,你可以在把整数放进字典之前先做个位旋转,然后在使用时再旋转回来。

这实际上就相当于你自己写了一个哈希函数。我想你可以在这里应用任何逻辑,把整数变成更适合哈希的形式。

比如:

from collections import Counter
import time

m = 2**61 - 1
n = 10000
# This will determine the maximum int you can safely rotate and the speed of your rotation operation
bit_format_string = "{:080b}"
bit_shift = 62


# TODO: Figure out a better way to do rotations
def rotate_left(x, n):
    return int(bit_format_string.format(x)[n:] + bit_format_string.format(x)[:n], 2)


def rotate_right(x, n):
    return int(bit_format_string.format(x)[-n:] + bit_format_string.format(x)[:-n], 2)


collide = [m * i for i in range(1, n + 1)]
shifted = [rotate_right(i, bit_shift) for i in collide]
s = time.time()
c = Counter(shifted)
e = time.time()
print(e-s)

# This restores the original values and gets you your counts
counts = [(rotate_left(k, bit_shift), v) for (k, v) in c.items()]

another = [k for k in range(n)]
s = time.time()
c = Counter(another)
e = time.time()
print(e-s)

这个方法也很快,并且能得到正确的输出。结果是:

0.0010600090026855469
0.0006046295166015625

而且这个测试通过了:assert set(t[0] for t in counts) == set(collide)

撰写回答