我对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
答案 0 :(得分:5)
SecureRandom
中使用的种子禁止在PID被回收时发生的可预测的随机数。如果没有SecureRandom
中的修复,OpenSSL的随机数生成器将在具有相同PID的不同进程中生成完全相同的值。
#4579概述了如何发生这种情况,并且OpenSSL邮件列表上的corresponding entry或多或少告诉我们必须在客户端代码中处理这个问题。这就是为什么在Ruby中选择这个种子来防止安全威胁的原因。如果不相信,请在此修复之前运行附加在Ruby版本上的script Eric Wong,看看这是什么。
除了owlstead的解释之外,此时播种OpenSSL的RNG不会危及安全性,因为未初始化的随机生成器将始终首先调用RAND_poll
,这将收集足够的熵,无论之前是否已经播种/添加了值或不。
但是,由于SecureRandom
中的种子值明显可预测,我们不应该假设他们添加任何熵。 OpenSSL的内部行为可能会在某些时候发生变化,如果已经播种的值被认为包含足够的熵,它可能会跳过初始熵收集。
总之,值的选择(PID和时间)是明智的,它甚至会增加整体安全性(通过防止“再循环PID攻击”)而不是减少它。
答案 1 :(得分:3)
这取决于使用RNG的Ruby的配置:
安全随机数生成器界面。
该库是安全随机数生成器的接口 适用于在HTTP cookie等中生成会话密钥。
它支持以下安全随机数生成器。
OpenSSL的
/dev/urandom
的Win32
上述所有三项通常都被认为是安全的。但是,如果它确实是安全的,它取决于SecureRandom
类的实现。了解这一点的唯一方法是对实施进行广泛的研究。
查看问题中的代码很明显Ruby在additionally播种PID之后直接使用OpenSSL生成的字节:
每当添加种子数据时,它将按如下方式插入“状态”。
将输入切换为20字节的单位(最后一个或更少) 块)。这些块中的每一个都通过哈希函数运行 如下:传递给哈希函数的数据是当前的'md',即 来自'state'的相同字节数(由in确定的位置) 递增的循环索引)作为当前的“块”,即新的密钥数据 'block'和'count'(每次使用后递增)。结果 这个保存在'md'中,同时也被保存在'state'中 用作哈希函数输入的位置。我相信 该系统解决了点1(散列函数;当前为SHA-1),3 ('state'),4(通过'md'),5(通过使用哈希函数和 XOR)。
答案 2 :(得分:2)
我的一位同事对此进行了调查,发现选择种子是为了回应这个错误:
http://bugs.ruby-lang.org/issues/4579
幸运的是,OpenSSL使用来自/ dev / urandom(如果可用)的256位熵,或者egd('熵收集守护进程' - / dev / urandom的前身),取决于它的编译方式。播种在第一次调用RAND_status()或RAND_bytes()时自动发生,如果显式调用RAND_seed(),则不会抑制播种。感谢OpenSSL人员做出这一决定。这是指向特定OpenSSL代码的链接:
http://cvs.openssl.org/dir?d=openssl/crypto/rand
有趣的文件是md_rand.c,rand_lib.c和rand_unix.c。