2012-08-25 44 views
8

我對Ruby知之甚少,所以請原諒我,如果這個答案很明顯。我注意到http://www.ruby-doc.org/stdlib-1.9.3/libdoc/securerandom/rdoc/SecureRandom.html Ruby在使用pid和當前時間調用random_bytes時播種OpenSSL :: Random。除非在封面下發生了其他事情,否則Netscape在90年代中期初始SSL實施中使用的種子是不是很多? http://en.wikipedia.org/wiki/Random_number_generator_attack#Prominent_examples_of_random_number_generator_security_issuesOpenSSL :: Random的Ruby種子是否足夠?

當然,Ruby還沒有恢復一個18歲的bug。我在這裏錯過了什麼?

編輯:這是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

它沒有很好的記錄,是嗎?看看OpenSSL提供的值是什麼,可以更仔細地查看源代碼。它應該使用任何OS級別的隨機設施,比如'/ dev/urandom'或'/ dev/random',而不是像這樣的東西。 – tadman

+0

注意到我發現了一些關於分叉如何導致漏洞的討論,這對於Ruby調用OpenSSL的方式來說並不是很好。 –

+0

你能指出它在哪裏聲明Ruby使用pid和當前時間作爲(唯一)種子? –

回答

3

這取決於所使用RNG紅寶石的配置:

安全隨機數生成器接口。

該庫是安全隨機數生成器,其 是適合於產生在HTTP餅乾會話密鑰等

它支持以下安全隨機數生成器的接口。

  • OpenSSL的

  • /dev/urandom

  • 的Win32

所有這三個以上的通常被認爲是安全的。但是,如果它實際上是安全的,則它取決於SecureRandom類的實現。瞭解這一點的唯一方法是對實現進行廣泛的研究。

尋找在問題很明顯,紅寶石直接使用由OpenSSL的產生字節代碼,additionally播種PID後:

每當添加種子數據,它被插入到「狀態」如如下。

將輸入切成20個字節的單位(對於最後一個 區塊)。這些塊中的每一個都通過散列函數運行,如下所示:傳遞給散列函數的數據是當前'md',與'狀態'相同的字節數(由 確定的位置遞增循環索引)作爲當前的「塊」,新的密鑰數據 「塊」和「計數」(在每次使用後遞增)。其結果 保留在'md'中,並且在用作散列函數的輸入的相同位置處也被嵌入到'狀態'中。我相信 這個系統處理點1(散列函數;當前SHA-1),3 ('狀態'),4(通過'md'),5(通過使用散列函數和異或)。

+0

安全性取決於實際的實施和底層平臺,但這應該是顯而易見的。 –

+0

特別是,它取決於所涉及的prng是否正確播種,以及呼叫者是否負責提供這樣的種子,或者如果prng種子本身。 – iamtheneal

+0

@iamtheneal如果我看看[SecureRandom.rb](http://uuidtools.rubyforge。org/coverage/lib-compat-securerandom_rb.html)代碼依賴openssl庫或操作系統的標準種子,如果庫/操作系統沒有問題的話,這應該是非常好的。 –

2

我的同事研究了這一點,並發現,種子的選擇被引入作爲到這個錯誤的響應:

http://bugs.ruby-lang.org/issues/4579

幸運的是,OpenSSL的種子本身與來自/熵的256位dev/urandom(如果可用)或者egd('entropy gather守護進程' - 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。

5

SecureRandom中使用的種子禁止每當PID被回收時發生的可預測隨機數。如果沒有在SecureRandom中修復,OpenSSL的隨機數生成器將在具有相同PID的不同進程中生成完全相同的值。

#4579概述瞭如何發生這種情況,而OpenSSL郵件列表上的corresponding entry告訴我們或多或少地要求在客戶端代碼中處理這個問題。這就是爲什麼在Ruby中選擇這個種子來防止安全威脅。如果不確定,請在此修補程序之前運行附帶在Ruby版本上的script Eric Wong,以查看這是什麼。

添加到owlstead的解釋,播種OpenSSL的RNG在這一點上不妥協的安全性,因爲未初始化的隨機數生成器總是會調用RAND_poll第一,這將收集到足夠的熵無論是否值已經先前接種的/添加或不。

但是,由於SecureRandom中的種子值是明確可預測的,因此我們不應該假定它們添加了任何熵。 OpenSSL的內部行爲可能會在某些時候發生變化,如果已經接種的值被認爲包含足夠的熵,它可以跳過初始熵收集。

因此,我打開了#6928,它會選擇一種更加防禦性的方法,假設沒有熵值加入到熵池中區分不同進程 - 這將迫使OpenSSL在所有情況下都可靠地收集足夠的熵。總之,值(PID和時間)的選擇是合理的,它甚至增加了總體安全性(通過防止「再循環PID攻擊」)而不是減小它。

+0

我同意。無可否認,我的言辭是不必要的,適得其反。在一天結束時,我認爲這是一個文檔問題。它*有點難以確認Ruby的SecureRandom實現不會降低安全性。 無論如何,感謝您的意見。另外,我編輯了我的答案,以減少煽動性。 – iamtheneal

+0

@iamtheneal謝謝!我刪除了評論並修改了我的答案。 – emboss