Joven hacker sonriendo

Generar Números Aleatorios Como Identificadores

Nuestros ethical hackers explican cómo evitar vulnerabilidades de seguridad mediante la programación segura en Java al generar números aleatorios como identificadores. Los números aleatorios seguros deben tener un valor semilla no predecible, ésto se logra con la clase SecureRandom.

Necesidad

Generar números, cadenas o sales aleatorias de manera segura en Java

Contexto

A continuación se describen las circunstancias bajo las cuales la siguiente solución tiene sentido:

  1. Se está desarrollando una aplicación en Java 1.4 o superior.

  2. La aplicación necesita generar números aleatorios usados como identificadores que no se almacenan.

Solución

En Java, la clase java.security.SecureRandom proporciona un generador de números pseudoaleatorios que cumplen, por lo menos, con la especificación FIPS 140-2 sobre pruebas estadísticas para generadores de números aleatorios y requisitos de seguridad de los módulos criptográficos [1].

Adicionalmente, la clase debe producir una salida no determinística, por lo tanto, requiere que la semilla no sea predecible y que la salida sean secuencias criptográficamente fuertes tal como está descrito en RFC 1750: Recomendaciones de aleatoriedad para la seguridad [2].

  1. Para entender la diferencia entre utilizar la clase Random y SecureRandom, considérese el siguiente par de programas.

    Programa1.java
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    // Programa 1
    import java.util.Random;
    
    class R {
      public static void main(String... args) {
        int seed = 123;
        Random rand = new Random(123);
        for(int i=0; i<5; i++) {
          System.out.print(rand.nextInt(5) + " ");
        }
      }
    }
    
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    import java.util.Random;
    import java.security.SecureRandom;
    
    class SR {
      public static void main(String... args) {
        byte[] seed = {123};
        Random rand = new SecureRandom(seed);
        for(int i=0; i<5; i++) {
          System.out.print(rand.nextInt(5) + " ");
        }
      }
    }
    
  2. Ambos programas muestran una secuencia de 5 enteros a partir de una semilla dada.

  3. Al ejecutar el primer programa repetidamente (haciendo uso de la clase Random), se observa que la salida es siempre la misma:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    % javac R.java
    
    % java R
    2 0 1 4 0
    
    % java R
    2 0 1 4 0
    
    % java R
    2 0 1 4 0
    
  4. El segundo caso, haciendo uso de la clase SecureRandom, se observa que la salida si es aleatoria.

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    % javac SR.java
    
    % java SR
    4 0 4 2 2
    
    % java SR
    4 1 4 1 4
    
    % java SR
    2 1 2 4 3
    
  5. Si bien es posible usar la clase Random con semillas cambiantes (como por ejemplo el epoch de Unix, tiempo desde 00:00:00 UTC del 1 de Enero de 1970), al no ser esta semilla realmente aleatoria sino, por el contrario, determinista, se podría predecir los números generados si se conoce, por ejemplo, cuanto lleva la aplicación en ejecución. En cambio usando SecureRandom esto no ocurre.

  6. Sin embargo, una de las desventajas de usar SecureRandom, es que consume mayor cantidad de recursos que Random [4]. Otra desventaja es que las llamadas a SecureRandom pueden causar bloqueos temporales en caso de que no haya suficiente entropía en el sistema [5].

  7. Para generar enteros de forma aleatoria se recomienda hacer uso de los métodos de la forma nextX que, a pesar de que algunos son heredados desde Random, su implementación garantiza que se usen los mecanismos propios de la clase SecureRandom [6], [7].

  8. A continuación se presenta un código que usa distintos métodos para generar enteros de manera segura.

  9. Para hacer uso de la clase SecureRandom primero se importa la clase de la biblioteca security.

    1
    import java.security.SecureRandom;
    
  10. A continuación, se muestra como utilizar los métodos nextInt y nextBytes de SecureRandom para generar números, arreglos de bytes y cadenas de caracteres aleatorias.

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    import javax.xml.bind.DatatypeConverter;
    
    class CLI {
      public static void main(String... args) {
        SecureRandom random = new SecureRandom();
        for(int i=0; i<3; i++) {
          System.out.println("Random integer: " + random.nextInt(Integer.MAX_VALUE));
          byte seed[] = new byte[48];
          random.nextBytes(seed);
          String encodedSeed = DatatypeConverter.printBase64Binary(seed);
          System.out.println("Base 64 encoded seed: " + encodedSeed);
          System.out.println("---");
        }
      }
    }
    
  11. El método nextInt retorna un valor entre 0 (incluyente) y el valor indicado como argumento (excluyente).

  12. El método nextBytes genera un arreglo de bytes del tamaño especificado por el tamaño del argumento.

  13. Finalmente, para obtener un String aleatorio, basta con aplicar cualquier codificación de caracteres al arreglo de bytes. En este caso se usa Base64.

  14. La salida, luego de dos ejecuciones, revela que las secuencias son realmente aleatorias.

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    % javac CLI.java
    
    % java CLI
    Random integer: 393546801
    Base 64 encoded seed:
    es3RpXzIFRu3FdMctXLc2F4tjxzy59lH5WrOQE7sK9FHlIcRlg6WtYL9lY8WQftJ
    
    Random integer: 1761515243
    Base 64 encoded seed:
    zSJd1VzJUre8Ky5MBdU6y9t1cqVk3bJDgJWFDdHh9f21+sqwoqm4sc+HsJktUwHo
    
    Random integer: 1551436295
    Base 64 encoded seed:
    HGJSi4oKze1kCdahO9Nnw8ThpRxz4PC1m9eMwpFeglPVpceH9EYmDHGp/4YjQjTg
    
    % java CLI
    Random integer: 800204432
    Base 64 encoded seed:
    8mIwchMkCDNLPpOGdTZlRNrpAW8hI6498loMCs170ZahDsASx0RSFIzbSGkaQA0Q
    
    Random integer: 386011948
    Base 64 encoded seed:
    h44JuaZdlp2qvPKJkve2cqc+iNuzeo6cbSZwcbg0pXYuBmeb49wi+NUWZx7wasmz
    
    Random integer: 1572155761
    Base 64 encoded seed:
    Hr9R7g0cLtTfcPXvQ5g0mOCXyItZKkg0o7ZQLbFsmcQNZHrvtc6gvS8KY2VGq6Es
    



Haz un comentario

Estado de los servicios - Términos de Uso