2012-08-25 10 views
8

So molto poco di Ruby, quindi per favore perdonami se la risposta è ovvia. Ho notato a http://www.ruby-doc.org/stdlib-1.9.3/libdoc/securerandom/rdoc/SecureRandom.html che Ruby usa il pid e l'ora corrente per seminare OpenSSL :: Random quando viene effettuata una chiamata a random_bytes. A meno che non succeda qualcos'altro sotto le copertine, non è questo il seme che Netscape ha usato nella loro implementazione SSL iniziale a metà degli anni '90? http://en.wikipedia.org/wiki/Random_number_generator_attack#Prominent_examples_of_random_number_generator_security_issuesIl seme di Ruby per OpenSSL :: Random è sufficiente?

Sicuramente Ruby non ha rianimato un bug di 18 anni. Cosa mi manca qui?

Modifica: ecco la fonte per random_bytes. Notare il primo controllo per vedere se Ruby è stato compilato con OpenSSL, nel qual caso lo semina con il pid e l'ora corrente.

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

Non è molto ben documentato, vero? Sarebbe educativo guardare più da vicino all'origine per vedere cosa fa OpenSSL con i valori forniti. Si suppone che utilizzi qualsiasi funzione casuale a livello di OS disponibile, come '/ dev/urandom' o'/dev/random 'invece di qualcosa del genere. – tadman

+0

NOte che ho trovato alcune discussioni su come il biforcarsi possa causare vulnerabilità, il che non è di buon auspicio per il modo in cui Ruby chiama OpenSSL. –

+0

Potrebbe indicare dove afferma che Ruby usa il pid e l'ora corrente come (solo) semi? –

risposta

3

Essa dipende dalla configurazione di rubino che RNG è utilizzato:

sicura numeri casuali interfaccia generatore.

Questa libreria è un'interfaccia per il generatore di numeri casuali sicuri che è adatto per la generazione di chiave di sessione nei cookie HTTP, ecc

Esso supporta i seguenti generatori di numeri casuali sicuri.

  • OpenSSL

  • /dev/urandom

  • Win32

Tutti e tre di cui sopra sono generalmente considerati sicuri. Tuttavia, dipende dall'implementazione della classe SecureRandom se è effettivamente protetta. L'unico modo per saperlo è una ricerca approfondita sulle implementazioni.

Guardando il codice in questione è chiaro che Ruby utilizza direttamente byte generati da OpenSSL, dopo additionally semina il PID:

Quando i dati vengono aggiunti seme, viene inserito nella 'stato' come segue.

L'input viene ridotto in unità di 20 byte (o meno per l'ultimo blocco ). Ognuno di questi blocchi viene eseguito tramite la funzione hash come segue : i dati passati alla funzione hash sono l'attuale 'md', lo stesso numero di byte dallo 'stato' (la posizione determinata da in indice di loop incrementato) come il "blocco" corrente, i nuovi dati chiave "blocco" e "conteggio" (che viene incrementato dopo ogni utilizzo). Il risultato di questo viene mantenuto in 'md' e anche xored nello 'stato' nelle stesse posizioni utilizzate come input nella funzione hash. Credo che questo sistema indirizzi punti 1 (funzione di hash, attualmente SHA-1), 3 (lo 'stato'), 4 (tramite 'md'), 5 (con l'uso di una funzione di hash e xor).

+0

Quanto è sicuro dipende dall'impenetazione effettiva e dalla piattaforma sottostante, ma dovrebbe essere ovvio. –

+0

In particolare, dipende se il prng in questione è seminato correttamente e se il chiamante è responsabile o meno della fornitura di tale seme, o se il seme si semina. – iamtheneal

+0

@iamtheneal se guardo il [SecureRandom.rb] (http: //uuidtools.rubyforge.org/coverage/lib-compat-securerandom_rb.html) codice che si basa sulla semina standard della libreria openssl o del sistema operativo, che dovrebbe essere assolutamente soddisfacente se la libreria/sistema operativo è a posto. –

2

Un mio collega indagato questo, e ha scoperto che la scelta delle sementi è stato introdotto come una risposta a questo bug:

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

Fortunatamente, OpenSSL si semi con 256 bit di entropia da/dev/urandom (se disponibile), o egd ('entropy gathering daemon' - un precursore di/dev/urandom) a seconda di come è stato compilato. Il seeding avviene automaticamente la prima volta che viene chiamato RAND_status() o RAND_bytes() e non viene soppresso se RAND_seed() viene chiamato esplicitamente. Complimenti per la gente OpenSSL per questa decisione. Ecco un link al codice di OpenSSL specifica:

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

I file interessanti sono md_rand.c, rand_lib.c e rand_unix.c.

5

Il seme utilizzato in SecureRandom proibisce i numeri casuali prevedibili che si verificano ogni volta che i PID vengono riciclati. Senza la correzione in SecureRandom, il generatore di numeri casuali di OpenSSL produrrà gli stessi identici valori in processi diversi che possiedono lo stesso PID.

#4579 descrive come ciò può accadere e corresponding entry sulla mailing list di OpenSSL ci dice più o meno che questo deve essere trattato nel codice client. Questo è il motivo per cui questo seme è stato scelto in Ruby per prevenire una minaccia alla sicurezza. Se non sei convinto, esegui il file script Eric Wong collegato a una versione di Ruby prima di questa correzione per vedere di cosa si trattava.

Aggiunta la spiegazione di owlstead, semina RNG di OpenSSL, a questo punto non compromettere la sicurezza, perché un generatore casuale non inizializzato sarà sempre chiamare RAND_poll prima, che sarà raccogliere abbastanza entropia indipendentemente dal fatto che i valori sono stati precedentemente seminate/aggiunti o meno.

Tuttavia, poiché i valori di inizializzazione in SecureRandom sono chiaramente prevedibili, non dovremmo assumere che aggiungano alcuna entropia. Il comportamento interno di OpenSSL potrebbe cambiare a un certo punto e potrebbe saltare la raccolta di entropia iniziale se si ritiene che i valori già seminati contengano abbastanza entropia.

Ho quindi aperto #6928, che sceglierebbe un approccio più difensivo di non assumere entropia per i valori aggiunti al pool di entropia che distinguono i diversi processi - questo costringerebbe OpenSSL a raccogliere in modo affidabile abbastanza entropia in tutti i casi.

In conclusione, la scelta dei valori (PID e tempo) è stata sensata, aggiunge anche alla sicurezza generale (impedendo l'attacco "PID riciclato") anziché diminuirla.

+0

Sono d'accordo. Devo ammettere che la mia retorica era inutile e controproducente. Alla fine della giornata, mi sembra che questo sia un problema di documentazione. Era * alquanto difficile confermare che l'implementazione di SecureRandom da parte di Ruby non stava riducendo la sicurezza. Comunque, grazie per il vostro contributo. Inoltre, ho modificato la mia risposta per renderla meno infiammatoria. – iamtheneal

+0

@iamtheneal Grazie! Ho rimosso il commento e modificato anche la mia risposta. – emboss

Problemi correlati