【问题标题】:Is python's random number generation easily reproducible?python的随机数生成容易重现吗?
【发布时间】:2015-09-30 03:06:24
【问题描述】:

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

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

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

并且当种子没有设置时,标准库种子使用time.time()。 现在假设一个在线服务使用random.random()生成验证码,黑客可以使用相同的随机生成器轻松复制验证码吗?

  1. 假设黑客知道将随机数转换为验证码的算法。否则,这似乎是不可能的。
  2. 由于在导入模块时调用了 random.seed(),我假设对于 Web 应用程序,用作种子的时间大约是发送请求的时间(几秒钟内),它不会是尝试几次就难以校准?

是我太担心了,还是这是一个真正的漏洞?

【问题讨论】:

    标签: python security web random


    【解决方案1】:

    播种后序列是确定性的,您应该不会感到惊讶。这就是播种的全部意义所在。 random.random 被称为 PRNG,一个伪-随机数生成器。这不是 Python 独有的,每种语言的简单随机源都是以这种方式确定的。

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

    但问题并没有你说的那么严重:对于一个 web 请求,通常一个进程会处理多个请求,因此该模块是在过去某个未知时间点初始化的,而不是在收到 web 请求时。

    【讨论】:

      【解决方案2】:

      现有的答案很好,但我只是补充几点。

      更新:

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

      random.seed(a=None, version=2)

      初始化随机数生成器。

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

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

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

      使用版本 1(提供用于从旧版本复制随机序列 Python 版本),str 和 bytes 的算法会生成一个 种子范围更窄。

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


      与生成加密密钥(尤其是打算多次使用的密钥)相比,生成验证码并不是一种高安全性应用程序。因此,生成 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() 来获取种子。那样的话,种子更不可预测,但你得到梅森捻线机的速度; 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 密钥。

      有关为什么 /dev/urandom 几乎总是比 /dev/random 更可取的更多信息,请参阅Myths about /dev/urandom

      【讨论】:

      • 感谢代码和参考!信息量很大。
      【解决方案3】:

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

      请参阅this answer 以获取安全随机数。

      【讨论】:

        【解决方案4】:

        Python documentation 有话要说:

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

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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2017-12-25
          • 2015-02-16
          • 2021-08-14
          • 1970-01-01
          • 2020-07-04
          • 1970-01-01
          • 2011-01-05
          • 1970-01-01
          相关资源
          最近更新 更多