hash() 的随机化是否被认为是密码学上强的?
从 CPython 3.3 开始,哈希随机化默认是开启的。在之前的版本中,可以通过指定 -R
命令行选项 或者设置 PYTHONHASHSEED
环境变量 为 random
来开启这个功能。
根据 文档 的说明:
默认情况下,字符串、字节和日期时间对象的
__hash__()
值会被一个不可预测的随机值“加盐”。虽然在一个Python进程中这些值是固定的,但在多次启动Python时,它们是不可预测的。
这是否意味着生成的值会很强大,像加密那样安全呢?
2 个回答
在3.4版本之前,Python使用了一种叫FNV的哈希算法,这种算法并不安全。简单地说,Python尝试给这个弱哈希函数加上一个随机值,但这样并不能真正提供安全性。因为这个哈希算法本身的弱点,即使加了随机值,我们仍然可以很容易地生成一些字符串,它们的FNV哈希值是一样的。
值得注意的是,即使这个随机值是完全随机的,并且没有泄露给用户,这种情况依然成立。
为了理解这个问题,可以想象一个非常简单的哈希函数——就是把字符串中的所有字符加起来。如果在字符串前面加一个随机值,那么任何单独的字符串的哈希值都会是随机的。然而,如果有两个字符串的字符加起来的总和是一样的,那么它们的哈希值也会是一样的,不管这个随机值是什么。因此,随机值并不能防止哈希冲突。Python实际上做的并没有这么糟糕,但也没有好到哪里去。
在3.4版本中,Python把默认的哈希算法换成了SipHash,这种算法被认为在抵御碰撞型拒绝服务攻击方面是安全的。不幸的是,使用2.x版本的人就完全没办法了。
在Python 3.3版本中,哈希种子并不够安全,它是在程序启动时生成的,使用的是一种伪随机生成器:
/* Fill buffer with pseudo-random bytes generated by a linear congruent
generator (LCG):
x(n+1) = (x(n) * 214013 + 2531011) % 2^32
Use bits 23..16 of x(n) to generate a byte. */
static void
lcg_urandom(unsigned int x0, unsigned char *buffer, size_t size)
{
size_t index;
unsigned int x;
x = x0;
for (index=0; index < size; index++) {
x *= 214013;
x += 2531011;
/* modulo 2 ^ (8 * sizeof(int)) */
buffer[index] = (x >> 16) & 0xff;
}
}
这种生成器并不是安全性很强的。
此外,关于哈希种子还有其他问题,这些问题使得可以强行制造哈希碰撞。
Python 3.4版本解决了这些问题,引入了一种更安全的哈希算法作为默认选项,并且可以根据需要进行替换。
如果你在程序中需要安全性强的随机数,可以使用random.SystemRandom()
或者os.urandom()
。