2012-08-25 13 views
8

Wiem bardzo niewiele o Ruby, więc proszę wybaczyć, jeśli odpowiedź na to pytanie jest oczywista. Zauważyłem na http://www.ruby-doc.org/stdlib-1.9.3/libdoc/securerandom/rdoc/SecureRandom.html, że Ruby używa pid i aktualnego czasu do wysiewu OpenSSL :: Random, gdy wykonywane jest połączenie z random_bytes. Chyba że coś innego dzieje się pod osłonami, czy nie jest to materiał siewny, który Netscape używał w początkowej implementacji SSL w połowie lat 90.? http://en.wikipedia.org/wiki/Random_number_generator_attack#Prominent_examples_of_random_number_generator_security_issuesCzy nasienie Ruby dla OpenSSL :: Losowo wystarczające?

Z pewnością Ruby nie wskrzesił 18-letniego błędu. Czego tu mi brakuje?

Edytuj: Oto źródło random_bytes. Zwróć uwagę na pierwsze sprawdzenie, czy ruby ​​zostało skompilowane z OpenSSL, w takim przypadku zasieje go z pid i bieżącym czasem.

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

To nie jest dobrze udokumentowane, prawda? Byłoby edukacyjnym przyjrzeć się bliżej źródłowi, aby zobaczyć, co robi OpenSSL z podanymi wartościami. Powinien używać dowolnych obiektów losowych na poziomie OS, takich jak '/ dev/urandom' lub'/dev/random' zamiast czegoś podobnego. – tadman

+0

NOte, że znalazłem jakąś dyskusję na temat tego, jak rozwidlenia mogą skutkować lukami w zabezpieczeniach, co nie wróży dobrze sposobowi, w jaki Ruby nazywa OpenSSL. –

+0

Czy możesz wskazać, gdzie stwierdza, że ​​Ruby używa pid i bieżącego czasu jako (tylko) nasiona? –

Odpowiedz

3

To zależy od konfiguracji Ruby, który jest używany RNG:

bezpiecznego interfejsu generatora liczb losowych.

Ta biblioteka jest interfejsem dla bezpiecznego Generator liczb losowych, które nadaje się do generowania klucza sesji w ciasteczkach HTTP, itp

Obsługuje następującym bezpiecznych generatorów liczb losowych.

  • openssl

  • /dev/urandom

  • Win32

Wszystkie trzy powyższe są ogólnie uważane za bezpieczne. Jednak zależy to od implementacji klasy SecureRandom, jeśli rzeczywiście jest bezpieczna. Jedynym sposobem na poznanie tego jest obszerne badanie implementacji.

Patrząc na kod w pytaniu jasno wynika, że ​​Ruby bezpośrednio używa bajtów generowanych przez OpenSSL, po additionally wysiewu PID:

Ilekroć dane ziarno dodaje, że jest włożona do „państwa” jako następuje.

Dane wejściowe są podzielone na jednostki o wielkości 20 bajtów (lub mniej dla ostatniego bloku ). Każdy z tych bloków przebiega przez funkcję skrótu, tak jak następuje: : Dane przekazywane do funkcji mieszającej to bieżący 'md', ta sama liczba bajtów ze "stanu" (lokalizacja określona w inkrementowanym indeksie pętli) jako bieżący "blok", nowe kluczowe dane "blok" i "licznik" (który jest zwiększany po każdym użyciu). Wynik tego jest trzymany w 'md', a także xored do "stanu" w tych samych lokalizacjach, które zostały użyte jako dane wejściowe do funkcji skrótu. Wierzę, że ten system odnosi się do punktów 1 (funkcja skrótu, obecnie SHA-1), 3 ("stan"), 4 (przez "md"), 5 (za pomocą funkcji skrótu i ​​ xor).

+0

Jak bezpieczne zależy od faktycznej impenetacji i platformy bazowej, ale to powinno być oczywiste. –

+0

W szczególności zależy to od tego, czy dany prng jest prawidłowo rozstawiony oraz od tego, czy osoba wywołująca jest odpowiedzialna za dostarczenie takiego materiału siewnego, czy też samo nasiona prng. – iamtheneal

+0

@iamtheneal, jeśli spojrzę na [SecureRandom.rb] (http: //uuidtools.rubyforge.org/coverage/lib-compat-securerandom_rb.html) kod opiera się na standardowym uruchomieniu biblioteki openssl lub systemu operacyjnego, co powinno być absolutnie w porządku, jeśli biblioteka/system operacyjny jest w porządku. –

2

Kolega badane tym i stwierdzono, że dobór nasion wprowadzono jako odpowiedzi na błędy:

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

szczęście, nasiona OpenSSL sam w sobie z 256 bitów entropii z/dev/urandom (jeśli jest dostępny) lub egd ("demon zbierania entropii" - prekursor do/dev/urandom) w zależności od sposobu kompilacji. Wysiew odbywa się automatycznie przy pierwszym wywołaniu funkcji RAND_status() lub RAND_bytes() i nie jest wyłączany, jeśli funkcja RAND_seed() jest wywoływana jawnie. Kudos dla ludzi z OpenSSL za tę decyzję. Oto link do konkretnego kodu OpenSSL:

http://cvs.openssl.org/dir?d=openssl/crypto/rand

Ciekawe pliki są md_rand.c, rand_lib.c i rand_unix.c.

5

Nasiona używane w SecureRandom zabraniają przewidywalnych liczb losowych, które występują, gdy PID zostaną poddane recyklingowi. Bez poprawki w SecureRandom generator liczb losowych OpenSSL będzie generował dokładnie te same wartości w różnych procesach, które mają ten sam PID.

#4579 określa, w jaki sposób może się to zdarzyć, a corresponding entry na liście adresowej OpenSSL mówi nam mniej więcej, że należy to załatwić w kodzie klienta. Właśnie dlatego ten materiał siewny został wybrany w Ruby, aby zapobiec zagrożeniu bezpieczeństwa. Jeśli nie jesteś przekonany, uruchom Eric Wong script dołączony do wersji Ruby przed tą poprawką, aby zobaczyć, o co w tym wszystkim chodzi.

Dodawanie do wyjaśnienia owlstead, w siewie RNG OpenSSL w tym momencie nie zagrozi bezpieczeństwu, ponieważ niezainicjowany generator losowy zawsze zadzwonić RAND_poll pierwszym, który zbierze wystarczającą entropię niezależnie od wartości zostały wcześniej zaszczepiono/dodane lub nie.

Jednak, ponieważ wartości początkowe w SecureRandom są wyraźnie przewidywalne, nie powinniśmy zakładać, że dodają jakąkolwiek entropię. Zachowanie wewnętrzne OpenSSL może w pewnym momencie ulec zmianie i może pominąć początkowe zbieranie entropii, jeśli wartości już zaszczepione są uważane za zawierające wystarczającą entropię.

Dlatego otworzyłem , który wybrałby bardziej defensywne podejście zakładające brak entropii dla wartości dodanych do puli entropii, które rozróżniają różne procesy - zmusiłoby to OpenSSL do niezawodnego gromadzenia wystarczającej entropii we wszystkich przypadkach.

Podsumowując, wybór wartości (PID i czas) był rozsądny, nawet zwiększa ogólne bezpieczeństwo (zapobiegając "powrotnemu atakowi PID") zamiast go zmniejszać.

+0

Zgadzam się. Prawdą jest, że moja retoryka była niepotrzebna i bezproduktywna. Pod koniec dnia wydaje mi się, że jest to kwestia dokumentacji. To * było * nieco trudne do potwierdzenia, że ​​wdrożenie SecureRandom przez Ruby nie zmniejszyło bezpieczeństwa. W każdym razie, dziękuję za twoje wejście. Zmieniłem też moją odpowiedź, aby było mniej zapalne. – iamtheneal

+0

@iamtheneal Dziękujemy! Usunąłem komentarz i zmodyfikowałem również odpowiedź. – emboss

Powiązane problemy