random.choice 不随机
我在Linux上使用Python 2.5,并且在多个并行的FCGI进程中工作。我使用
chars = string.ascii_letters + string.digits
cookie = ''.join([random.choice(chars) for x in range(32)])
来生成不同的cookie。假设随机数生成器是从/dev/urandom获取种子的,并且随机数的序列是来自梅森旋转算法,我本以为碰撞的可能性几乎为零。
然而,我确实看到经常发生碰撞,尽管同时登录的用户只有少于100个。
为什么这些随机数没有更随机呢?
5 个回答
我不知道你的FCGI进程是怎么启动的,但有可能是在Python解释器启动后使用了fork(),而且在这之前随机模块已经被某个东西导入了,这样就导致两个进程的
random._inst
是从同一个源头生成的。也许可以加一些调试代码,检查一下它是否正确地从urandom获取种子,而不是退回到不那么严格的基于时间的种子?
补充评论:哎呀!这让我很困惑;如果随机数生成器在启动时总是有不同的状态,我就看不出怎么会出现冲突。真奇怪。可能需要记录很多状态信息来调查导致冲突的具体情况,这听起来像是要花很多时间去翻阅日志。可能是(1a)FCGI服务器通常不使用fork,但偶尔会在负载较高时使用?
或者(3)是一些更高层次的问题,比如一个坏的HTTP代理把同一个Set-Cookie发送给多个客户端?
这绝对不是一个正常的碰撞情况:
- 32个字符,每个字符有62种选择,这相当于190位(计算方式是log2(62) * 32)
- 根据生日悖论,你应该每2的95次方的“饼干”中自然会遇到一次碰撞,这意味着几乎不可能发生
这可能是并发问题吗?
- 如果是的话,建议每个线程使用不同的
random.Random
实例 - 可以将这些实例保存在线程本地存储中(
threading.local()
) - 在Linux系统上,Python应该使用
os.urandom()
来初始化这些实例,而不是系统时间,这样每个线程就能得到不同的随机数流。
不应该生成重复的内容。
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
如果字符集是"ab",那么在1000000次尝试中,重复的可能性很大,达到了126次重复。但是如果字符集是62,就几乎没有重复的情况。
不过,这种方法并不适合用来生成会话cookie,因为会话cookie需要是不可预测的,这样才能防止有人偷取其他人的会话cookie。梅森旋转算法并不是为了生成安全的随机数而设计的。我通常会这样做:
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)。