python的随机数生成是否易于重现?

时间:2015-07-11 11:00:20

标签: python security web random

我正在阅读标准库中有关python的随机模块。令我惊讶的是,当我设置种子并产生一些随机数时:

random.seed(1)
for i in range(5):
    print random.random()

产生的数字与文章中的样本完全相同。我认为可以肯定地说,当种子设定时,算法是确定性的。

当未设置种子时,标准库种子为time.time()。 现在假设在线服务使用random.random()生成验证码,黑客是否可以使用相同的随机生成器轻松再现验证码?

  1. 让我们假设黑客知道将随机数转换为验证码的算法。否则,似乎很不可能。
  2. 由于在导入模块时调用random.seed(),我假设对于Web应用程序,用作种子的时间大约是发送请求的时间(在几秒钟内),它不会几次尝试难以计算?
  3. 我是否担心太多,或者这是一个真正的漏洞?

4 个答案:

答案 0 :(得分:6)

播种后序列是确定性的,这不应该让你感到惊讶。这就是播种的重点。 random.random被称为PRNG,伪 - 随机数生成器。这不是Python独有的,每种语言的简单随机源都是以这种方式确定的。

是的,真正关心安全性的人会担心攻击者可以重现序列。这就是为什么其他随机源可用,如os.urandom,但它们更贵。

但问题并不像你说的那么糟糕:对于Web请求,通常一个进程处理多个请求,因此模块在过去的某个未知点初始化,而不是在收到Web请求时。< / p>

答案 1 :(得分:6)

现有答案很棒,但我只想补充几点。

<强>更新

实际上,如果你不提供种子,那么随机数生成器会从系统随机源中随机位进行播种,如果操作系统没有随机数,它只会回退到使用系统时间作为种子资源。另请注意,最新版本的Python可以使用改进的种子方案。来自the docs

  

random.seed(a =无,版本= 2)

     

初始化随机数生成器。

     

如果省略aNone,则使用当前系统时间。如果   随机源由操作系统提供,它们被使用   而不是系统时间(参见os.urandom()函数   可用性详情。

     

如果a是int,则直接使用。

     

使用版本2(默认值),str,bytes或bytearray对象获取   转换为int并使用它的所有位。

     

使用版本1(用于从旧版本中再现随机序列)   Python的版本,str和bytes的算法生成一个   种子范围较窄。

     

在版本3.2中更改:已移至版本2方案,该方案使用字符串种子中的所有位。

生成CAPTCHA代码并不是一种高安全性的应用程序,而是生成秘密加密密钥,尤其是旨在多次使用的密钥。作为推论,生成CAPTCHA代码所需的熵量小于加密密钥所需的熵量。

请记住,用于播种random的系统时间(可能)不是以秒为单位的系统时间 - 它更可能是以微秒甚至纳秒为单位的时间,因此攻击者并不容易除了Ned提到的考虑因素之外,还要从粗暴搜索中找出种子。

这是一个快速演示,在2GHz Linux系统上运行Python 2.6.6。

#!/usr/bin/env python
''' random seeding demo'''

from __future__ import print_function
import time
from random import seed, randint, random

def rf():
    return randint(10, 99)

def put_time():
    print('%.15f' % time.time())

r = range(10)
a = []

put_time()
for i in r:
    seed()
    a.append([rf() for j in r])
put_time()

for row in a:
    print(row)

典型输出

1436617059.071794986724854
1436617059.074091911315918
[95, 25, 50, 75, 80, 38, 21, 26, 85, 82]
[75, 96, 14, 13, 76, 53, 94, 68, 80, 66]
[79, 33, 65, 86, 12, 32, 80, 83, 36, 42]
[28, 47, 62, 21, 52, 30, 54, 62, 22, 28]
[22, 40, 71, 36, 78, 64, 17, 33, 99, 43]
[81, 15, 32, 15, 63, 57, 83, 67, 12, 62]
[22, 56, 54, 55, 51, 56, 34, 56, 94, 16]
[64, 82, 37, 80, 70, 91, 56, 41, 55, 12]
[47, 37, 64, 14, 69, 65, 42, 17, 22, 17]
[43, 43, 73, 82, 61, 55, 32, 52, 86, 74]

正如您所看到的,外环开始和外循环之间的时间不到3毫秒。它的结束,但a中的所有列表都完全不同。

请注意,传递给random.seed()的种子可以是任何可哈希的对象,当你传递一个非整数(例如像系统时间的float)时,它首先被哈希以创建一个整数。

但是,没有必要仅仅使用系统时间作为种子:您可以使用SystemRandom / os.urandom()来获取种子。这样,种子更难以预测,但你获得了Mersenne Twister的速度; SystemRandom比Mersenne Twister慢一点,因为它必须进行系统调用。但是,即使urandom也不是完全安全的。

来自GNU urandom man page

  

随机数发生器从设备收集环境噪音   驱动程序和其他来源进入熵池。发电机也   保持对熵池中噪声位数的估计。   从该熵池中创建随机数。

     

读取时,/ dev / random设备只返回随机字节   在熵池中估计的噪声比特数内。   / dev / random应该适合需要非常高质量的用途   随机性如一次性垫或密钥生成。当熵   池是空的,从/ dev / random读取将阻塞直到另外   收集环境噪音。

     

来自/ dev / urandom设备的读取不会阻止等待更多   熵。结果,如果没有足够的熵   熵池,返回值理论上容易受到影响   对驱动程序使用的算法进行加密攻击。知识   如何执行此操作在当前未分类中不可用   文献,但理论上可能是这样的攻击   存在。如果这是您的应用程序中的问题,请使用/ dev / random   代替。

     

<强>用法

     

如果您不确定是否应该使用   / dev / random或/ dev / urandom,那么可能你想使用后者。   作为一般规则,/ dev / urandom应该用于除了之外的所有内容   长期使用的GPG / SSL / SSH密钥。

答案 2 :(得分:5)

  

几乎所有模块函数都依赖于函数random(),它在半开放范围[0.0,1.0]内均匀生成随机浮点数。 Python使用Mersenne Twister作为核心生成器。它产生53位精度浮点数,周期为2 ** 19937-1。 C中的底层实现既快又线程安全。 Mersenne Twister是现存最广泛测试的随机数发生器之一。但是,完全确定性,它不适合所有目的,并且完全不适合加密目的

有关安全随机的信息,请参阅this answer

答案 3 :(得分:4)

Python documentation有这样说:

  

警告   不应使用此模块的伪随机生成器   安全目的。如果需要,请使用os.urandom()或SystemRandom   加密安全的伪随机数发生器。

因此,将它用于CAPTCHA可能不是一个好主意。