Ruby的OpenSSL :: Random的种子是否足够?

iam*_*eal 8 ruby security cryptography prng

我对Ruby知之甚少,所以如果答案很明显,请原谅我.我在http://www.ruby-doc.org/stdlib-1.9.3/libdoc/securerandom/rdoc/SecureRandom.html注意到,当调用random_bytes时,Ruby使用pid和当前时间来播种OpenSSL :: Random是.除非发生其他事情,否则这不是Netscape在90年代中期初始SSL实施中使用的种子吗? http://en.wikipedia.org/wiki/Random_number_generator_attack#Prominent_examples_of_random_number_generator_security_issues

当然Ruby还没有复活一个18岁的小虫.我在这里错过了什么?

编辑:这是random_bytes的来源.注意第一次检查是否使用OpenSSL编译ruby,在这种情况下,它会使用pid和当前时间对其进行种子处理.

def self.random_bytes(n=nil)
  n = n ? n.to_int : 16

  if defined? OpenSSL::Random
    @pid = 0 if !defined?(@pid)
    pid = $$
    if @pid != pid
      now = Time.now
      ary = [now.to_i, now.nsec, @pid, pid]
      OpenSSL::Random.seed(ary.to_s)
      @pid = pid
    end
    return OpenSSL::Random.random_bytes(n)
  end

  if !defined?(@has_urandom) || @has_urandom
    flags = File::RDONLY
    flags |= File::NONBLOCK if defined? File::NONBLOCK
    flags |= File::NOCTTY if defined? File::NOCTTY
    begin
      File.open("/dev/urandom", flags) {|f|
        unless f.stat.chardev?
          raise Errno::ENOENT
        end
        @has_urandom = true
        ret = f.readpartial(n)
        if ret.length != n
          raise NotImplementedError, "Unexpected partial read from random device: only #{ret.length} for #{n} bytes"
        end
        return ret
      }
    rescue Errno::ENOENT
      @has_urandom = false
    end
  end

  if !defined?(@has_win32)
    begin
      require 'Win32API'

      crypt_acquire_context = Win32API.new("advapi32", "CryptAcquireContext", 'PPPII', 'L')
      @crypt_gen_random = Win32API.new("advapi32", "CryptGenRandom", 'LIP', 'L')

      hProvStr = " " * 4
      prov_rsa_full = 1
      crypt_verifycontext = 0xF0000000

      if crypt_acquire_context.call(hProvStr, nil, nil, prov_rsa_full, crypt_verifycontext) == 0
        raise SystemCallError, "CryptAcquireContext failed: #{lastWin32ErrorMessage}"
      end
      @hProv, = hProvStr.unpack('L')

      @has_win32 = true
    rescue LoadError
      @has_win32 = false
    end
  end
  if @has_win32
    bytes = " ".force_encoding("ASCII-8BIT") * n
    if @crypt_gen_random.call(@hProv, bytes.size, bytes) == 0
      raise SystemCallError, "CryptGenRandom failed: #{lastWin32ErrorMessage}"
    end
    return bytes
  end

  raise NotImplementedError, "No random device"
end
Run Code Online (Sandbox Code Playgroud)

emb*_*oss 5

使用的种子SecureRandom禁止在PID被回收时发生的可预测的随机数.如果没有修复SecureRandom,OpenSSL的随机数生成器将在具有相同PID的不同进程中生成完全相同的值.

#4579概述了这种情况如何发生,OpenSSL邮件列表上的相应条目告诉我们,或多或少会在客户端代码中处理这个问题.这就是为什么在Ruby中选择这个种子来防止安全威胁的原因.如果不相信,在此修复之前运行脚本 Eric Wong附加在Ruby版本上,看看这是什么.

加上owlstead的解释,此时播种OpenSSL的RNG不会危及安全性,因为未初始化的随机生成器将始终RAND_poll首先调用,这将收集足够的熵,无论先前是否已经播种/添加了值.

但是,由于种子值SecureRandom明显可以预测,我们不应该假设他们添加任何熵.OpenSSL的内部行为可能会在某些时候发生变化,如果已经播种的值被认为包含足够的熵,它可能会跳过初始熵收集.

因此,我打开了#6928,它会选择一种更加防御性的方法,即假设没有熵来添加到熵池中来区分不同的过程 - 这将迫使OpenSSL在所有情况下可靠地收集足够的熵.

总之,值(PID和时间)的选择是明智的,它甚至增加了整体安全性(通过防止"再循环PID攻击")而不是减少它.


Maa*_*wes 3

使用哪个 RNG 取决于 Ruby 的配置:

安全随机数生成器接口。

该库是安全随机数生成器的接口,适用于在 HTTP cookie 等中生成会话密钥。

它支持以下安全随机数生成器。

  • 开放式SSL

  • /dev/urandom

  • Win32

上述所有三种通常被认为是安全的。SecureRandom然而,它是否确实安全取决于类的实现。了解这一点的唯一方法是对实现进行广泛的研究。

查看问题中的代码,很明显,在额外播种 PID 后,Ruby 直接使用 OpenSSL 生成的字节:

每当添加种子数据时,它都会被插入到“状态”中,如下所示。

输入被分割成 20 个字节的单元(或者最后一个块的字节更少)。这些块中的每一个都通过哈希函数运行,如下所示:传递给哈希函数的数据是当前的“md”,来自“状态”(由增量循环索引确定的位置)的字节数与当前的字节数相同。 'block'、新的关键数据'block'和'count'(每次使用后递增)。其结果保存在“md”中,并异或到“状态”中,位置与用作哈希函数输入的位置相同。我相信这个系统解决了点 1(哈希函数;当前为 SHA-1)、3(“状态”)、4(通过“md”)、5(通过使用哈希函数和异或)。