2012-08-25 10 views
8

Sé muy poco sobre Ruby, así que por favor, perdóneme si la respuesta es obvia. Noté en http://www.ruby-doc.org/stdlib-1.9.3/libdoc/securerandom/rdoc/SecureRandom.html que Ruby utiliza el pid y la hora actual para inicializar OpenSSL :: Random cuando se realiza una llamada a random_bytes. A menos que ocurra algo más bajo las sábanas, ¿no es esta la semilla que Netscape usó en su implementación inicial de SSL a mediados de los 90? http://en.wikipedia.org/wiki/Random_number_generator_attack#Prominent_examples_of_random_number_generator_security_issues¿La semilla de Ruby para OpenSSL :: Random es suficiente?

Surely Ruby no ha revivido un error de 18 años. ¿Que me estoy perdiendo aqui?

Editar: Aquí está la fuente para random_bytes. Observe la primera comprobación para ver si se compiló ruby ​​con OpenSSL, en cuyo caso lo inicializa con el pid y la hora actual.

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

No está muy bien documentado, ¿verdad? Sería educativo mirar más de cerca la fuente para ver qué hace OpenSSL con los valores proporcionados. Se supone que debe usar cualquier instalación aleatoria de nivel de sistema operativo disponible, como '/ dev/urandom' o'/dev/random' en lugar de algo así. – tadman

+0

NO He encontrado algo de discusión sobre cómo la bifurcación puede provocar vulnerabilidades, lo que no es un buen augurio para la forma en que Ruby llama a OpenSSL. –

+0

¿Podría indicarnos dónde dice que Ruby usa el pid y la hora actual como (solo) semillas? –

Respuesta

3

Depende de la configuración de rubí que se utiliza RNG:

número aleatorio interfaz generador Secure.

Esta biblioteca es una interfaz para el generador de números aleatorios seguro que es adecuado para la generación de clave de sesión en cookies HTTP, etc.

Soporta la siguiente generadores de números aleatorios seguras.

  • openssl

  • /dev/urandom

  • Win32

Los tres de lo anterior se considera generalmente seguro. Sin embargo, depende de la implementación de la clase SecureRandom si realmente es segura. La única forma de saber esto es una investigación exhaustiva de las implementaciones.

Si examina el código en la pregunta es evidente que Ruby utiliza directamente bytes generados por OpenSSL, después additionally sembrar el PID:

Cada vez que se añade los datos de la semilla, que se inserta en el 'estado' como sigue

La entrada se divide en unidades de 20 bytes (o menos para el último bloque ). Cada uno de estos bloques se ejecuta a través de la función hash como sigue : los datos pasados ​​a la función hash es el 'md' actual, el mismo número de bytes del 'estado' (la ubicación determinada por índice de bucle incrementado) como el 'bloque' actual, los nuevos datos clave 'bloque', y 'recuento' (que se incrementa después de cada uso). El resultado de esto se guarda en 'md' y también se transfiere al 'estado' en las mismas ubicaciones que se usaron como entrada en la función hash. Creo que este sistema aborda los puntos 1 (función hash, actualmente SHA-1), 3 (el 'estado'), 4 (a través del 'md'), 5 (mediante el uso de una función hash y xor).

+0

Qué tan seguro depende de la implementación real y la plataforma subyacente, pero eso debería ser obvio. –

+0

En particular, depende de si el prng en cuestión se siembra correctamente, y de si el que llama es responsable de proporcionar tal semilla, o si el prng se siembra por sí mismo. – iamtheneal

+0

@iamtheneal si miro el [SecureRandom.rb] (http: //uuidtools.rubyforge.org/coverage/lib-compat-securerandom_rb.html) code se basa en el siembra estándar de la biblioteca openssl o del sistema operativo, que debería ser absolutamente correcto si la biblioteca/sistema operativo está bien. –

2

Un colega mío investigó esta, y encontraron que la elección de las semillas se introdujo como una respuesta a este error:

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

Afortunadamente, OpenSSL semillas se clasifica con 256 bits de entropía desde/dev/urandom (si está disponible), o el egd ('entropy gathering daemon' - un precursor de/dev/urandom) según la forma en que se compiló. La siembra se produce automáticamente la primera vez que se llama a RAND_status() o RAND_bytes(), y no se suprime si se llama explícitamente a RAND_seed(). Felicitaciones a la gente de OpenSSL por esta decisión. Aquí hay un enlace al código OpenSSL específica:

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

Los archivos interesantes son md_rand.c, rand_lib.c y rand_unix.c.

5

La semilla que se usa en SecureRandom prohíbe los números aleatorios predecibles que se producen cada vez que se reciclan los PID. Sin la corrección en SecureRandom, el generador de números aleatorios de OpenSSL producirá exactamente los mismos valores en diferentes procesos que poseen el mismo PID.

#4579 describe cómo esto puede suceder, y corresponding entry en la lista de correo de OpenSSL nos dice más o menos que esto tiene que ser tratado en el código del cliente. Esta es la razón por la cual esta semilla fue elegida en Ruby para prevenir una amenaza a la seguridad. Si no está convencido, ejecute el script Eric Wong adjunto en una versión de Ruby antes de esta solución para ver de qué se trataba.

Agregando a la explicación de owlstead, sembrando RNG de OpenSSL en este punto no pone en peligro la seguridad, debido a que un generador aleatorio sin inicializar siempre llamará RAND_poll primera, que reunirá suficiente entropía independientemente de si los valores han sido previamente sembradas/añadido o no.

Sin embargo, dado que los valores de inicialización en SecureRandom son claramente predecibles, no deberíamos suponer que añaden ninguna entropía. El comportamiento interno de OpenSSL podría cambiar en algún momento y podría omitir la recopilación de entropía inicial si se considera que los valores ya sembrados contienen suficiente entropía.

Por lo tanto, abrí #6928, que elegiría un enfoque más defensivo al asumir que no hay entropía para los valores añadidos al grupo de entropía que distinguen diferentes procesos; esto forzaría a OpenSSL a recopilar suficiente entropía en todos los casos.

En conclusión, la elección de valores (PID y tiempo) fue razonable, incluso se suma a la seguridad general (al evitar el "ataque PID reciclado") en lugar de disminuirla.

+0

Estoy de acuerdo. Es cierto que mi retórica fue innecesaria y contraproducente. Al final del día, me parece que este es un problema de documentación. Era * un tanto difícil confirmar que la implementación de SecureRandom por parte de Ruby no estaba reduciendo la seguridad. De todos modos, gracias por su aporte. Además, he editado mi respuesta para que sea menos inflamatoria. – iamtheneal

+0

@iamtheneal ¡Gracias! Eliminé el comentario y también modifiqué mi respuesta. – emboss

Cuestiones relacionadas