Neo*_*ang 5 python security random web
我在标准库中阅读python的随机模块.令我惊讶的是,当我设置种子并产生一些随机数时:
random.seed(1)
for i in range(5):
print random.random()
Run Code Online (Sandbox Code Playgroud)
产生的数字与文章中的样本完全相同.我认为可以肯定地说,当种子设置时,算法是确定性的.
当种子未设置时,标准库种子用time.time().现在假设一个在线服务用于random.random()生成验证码,黑客可以使用相同的随机生成器轻松地重现验证码吗?
我是否担心太多,或者这是一个真正的漏洞?
播种后序列是确定性的,这不应该让你感到惊讶.这就是播种的重点. random.random被称为PRNG,伪随机数发生器.这不是Python独有的,每种语言的简单随机源都是以这种方式确定的.
是的,真正关心安全性的人会担心攻击者可以重现序列.这就是为什么其他随机来源可用的原因os.urandom,但它们更贵.
但问题并不像你说的那么糟糕:对于Web请求,通常一个进程处理多个请求,因此模块在过去的某个未知点初始化,而不是在收到Web请求时.
现有的答案很好,但我只补充几点。
更新:
实际上,如果您不提供种子,则随机数生成器会使用系统随机源中的随机位作为种子,如果操作系统没有随机源,则它只能使用系统时间作为种子。还要注意,Python的最新版本可以使用改进的种子计划。从文档:
random.seed(a =无,版本= 2)
初始化随机数生成器。
如果
a省略或None,则使用当前系统时间。如果操作系统提供了随机性源,那么将使用它们而不是系统时间(os.urandom()有关可用性的详细信息,请参见功能)。如果
a为int,则直接使用。对于版本2(默认值),str,bytes或bytearray对象将转换为int并使用其所有位。
对于版本1(为从旧版本的Python中复制随机序列而提供),用于str和bytes的算法生成的种子范围更窄。
在版本3.2中更改:移至版本2方案,该方案使用字符串种子中的所有位。
与生成秘密密码密钥(尤其是打算多次使用的密钥)相比,生成验证码不是一个高安全性的应用程序。必然地,生成验证码所需的熵量小于加密密钥所需的熵量。
请记住,用于播种的系统时间random(可能)不是以秒为单位的系统时间-更有可能是以微秒为单位的时间,甚至是以纳秒为单位的时间,因此对于攻击者而言,从野蛮中找出种子并不容易-搜索,除了内德提到的考虑。
这是一个快速演示,在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)
Run Code Online (Sandbox Code Playgroud)
典型输出
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]
Run Code Online (Sandbox Code Playgroud)
如您所见,外循环的开始与结束之间相隔不到3毫秒,但是其中的所有列表a都大不相同。
请注意,传递给的种子random.seed()可以是任何可哈希对象,当您将其传递给非整数对象(例如,float类似于系统时间)时,首先会对其进行哈希处理以创建一个整数。
仍然不需要仅将系统时间用作种子:您可以使用SystemRandom/ os.urandom()来获取种子。这样一来,种子就变得更加难以预测,但是您会得到Mersenne Twister的速度。SystemRandom比Mersenne Twister慢一点,因为它必须进行系统调用。但是,甚至也不urandom是完全安全的。
从GNU urandom手册页:
随机数发生器将来自设备驱动程序和其他来源的环境噪声收集到一个熵池中。发生器还保持熵池中噪声位数的估计。根据该熵池,创建随机数。
读取时,/ dev / random设备将仅返回熵池中估计的噪声位数内的随机字节。/ dev / random应该适合需要非常高质量的随机性的应用,例如一次性填充或密钥生成。当熵池为空时,从/ dev / random的读取将被阻塞,直到收集到其他环境噪声为止。
从/ dev / urandom设备进行的读取不会阻止等待更多的熵。结果,如果在熵池中没有足够的熵,则返回的值在理论上容易受到驱动程序使用的算法的加密攻击。当前未分类的文献中尚不提供有关如何执行此操作的知识,但从理论上讲,可能存在这种攻击。如果您的应用程序中有此问题,请改用/ dev / random。
用法
如果不确定是否应使用/ dev / random或/ dev / urandom,则可能要使用后者。通常,/ dev / urandom应该用于除长期使用的GPG / SSL / SSH密钥以外的所有内容。