hash() 的随机化是否被认为是密码学上强的?

3 投票
2 回答
1081 浏览
提问于 2025-04-18 04:54

CPython 3.3 开始,哈希随机化默认是开启的。在之前的版本中,可以通过指定 -R 命令行选项 或者设置 PYTHONHASHSEED 环境变量random 来开启这个功能。

根据 文档 的说明:

默认情况下,字符串、字节和日期时间对象的 __hash__() 值会被一个不可预测的随机值“加盐”。虽然在一个Python进程中这些值是固定的,但在多次启动Python时,它们是不可预测的。

这是否意味着生成的值会很强大,像加密那样安全呢?

2 个回答

1

在3.4版本之前,Python使用了一种叫FNV的哈希算法,这种算法并不安全。简单地说,Python尝试给这个弱哈希函数加上一个随机值,但这样并不能真正提供安全性。因为这个哈希算法本身的弱点,即使加了随机值,我们仍然可以很容易地生成一些字符串,它们的FNV哈希值是一样的。

值得注意的是,即使这个随机值是完全随机的,并且没有泄露给用户,这种情况依然成立。

为了理解这个问题,可以想象一个非常简单的哈希函数——就是把字符串中的所有字符加起来。如果在字符串前面加一个随机值,那么任何单独的字符串的哈希值都会是随机的。然而,如果有两个字符串的字符加起来的总和是一样的,那么它们的哈希值也会是一样的,不管这个随机值是什么。因此,随机值并不能防止哈希冲突。Python实际上做的并没有这么糟糕,但也没有好到哪里去。

在3.4版本中,Python把默认的哈希算法换成了SipHash,这种算法被认为在抵御碰撞型拒绝服务攻击方面是安全的。不幸的是,使用2.x版本的人就完全没办法了。

6

在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()

撰写回答