random.choice不是随机的

时间:2009-09-02 06:06:22

标签: python random

我在Linux上使用Python 2.5,在多个并行的FCGI进程中。我用

    chars = string.ascii_letters + string.digits
    cookie = ''.join([random.choice(chars) for x in range(32)])

生成不同的Cookie。假设RNG是从/ dev / urandom播种的,并且随机数序列来自Mersenne twister,我预计几乎没有碰撞机会。

但是,我确实看到了常规的冲突,即使只有少数(<100)用户在任何时间登录。

为什么随机数不是更随机?

5 个答案:

答案 0 :(得分:12)

它不应该产生重复。

import random
chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
def gen():
    return ''.join([random.choice(chars) for x in range(32)])

test = [gen() for i in range(100000)]
print len(test), len(set(test)) # 100000 100000

chars =“ab”,重复的可能性很大;在1000000次迭代中重复126次。它与62一样不存在。

也就是说,这不是生成cookie的好方法,因为会话cookie需要不可预测,以避免涉及窃取其他人的会话cookie的攻击。 Mersenne Twister不是为生成安全随机数而设计的。这就是我的工作:

import os, hashlib
def gen():
    return hashlib.sha1(os.urandom(512)).hexdigest()

test = [gen() for i in range(100000)]
print len(test), len(set(test))

...这应该是非常安全的(也就是说,难以获取一串会话cookie并猜测其他现有的会话cookie)。

答案 1 :(得分:4)

这绝对不是正常的碰撞场景:

  • 每个字符有62个选项的32个字符相当于190位(log2(62)* 32)
  • 根据生日悖论,你应该每2 ** 95饼干自然发生一次碰撞,这意味着永远不会

这可能是并发问题吗?

  • 如果是,请为每个线程使用不同的random.Random个实例
  • 可以将这些实例保存在线程本地存储(threading.local()
  • 在Linux上,Python应该使用os.urandom()来播种它们 - 而不是系统时间 - 所以你应该为每个线程获得不同的流。

答案 2 :(得分:1)

  1. 我不知道你的FCGI进程是如何产生的,但是在Python解释器启动后它是否可能使用fork()(随机模块已被某些东西导入),因此有效播种来自同一来源的两个进程'random._inst

  2. 也许进行一些调试,检查它是否正确地从urandom播种,而不是回到不那么严格的基于时间的种子?

  3. eta re comment:男人!那是我难倒的;如果RNG在启动时总是有不同的状态,我看不出你怎么可能发生碰撞。奇怪的。我想,必须进行大量的状态记录来调查导致冲突的特定情况,这听起来像是通过日志进行的大量工作。可能是(1a)FCGI服务器通常不会分叉,但偶尔会(可能是负载或其他东西)吗?

    或者(3)某些更高级别的问题,例如破坏的HTTP代理将同一个Set-Cookie传递给多个客户端?

答案 3 :(得分:0)

我必须删除我的原始答案,这表明生成器不是来自/dev/urandom,因为source(对于Python 3.x)明确表示它是:

def seed(self, a=None):
    """Initialize internal state from hashable object.

    None or no argument seeds from current time or from an operating
    system specific randomness source if available.

    If a is not None or an int or long, hash(a) is used instead.
    """

    if a is None:
        try:
            a = int(_hexlify(_urandom(16)), 16)
        except NotImplementedError:
            import time
            a = int(time.time() * 256) # use fractional seconds

    super().seed(a)
    self.gauss_next = None
因此,我谦卑地接受世界上有一些我可能无法解读的谜团。

答案 4 :(得分:-4)

为避免此问题,您可以使用一系列保证不同的Cookie(例如,您可以使用一组)。每次向某人提供cookie时,都会从序列中获取cookie,然后再添加另一个cookie。另一种选择是生成UUID并将其用作cookie。

避免此问题的另一种方法可能是保留私钥,并使用私钥的(例如MD5)校验和,并将计数器值加入其中。碰撞的概率将非常低。为了更安全,在校验和中添加一些变量,比如当前时间,用户的IP地址,......

存在生成cookie的库。任何WSGI实现都可能包含cookie生成器。

如果您只对字符串的随机性感兴趣,可以生成一个文件,例如一百万个Cookie,并对该文件执行随机检查。但是,这不是我的建议。