Joven hacker sonriendo

Hackeamos su software

cero falsos positivos

Inteligencia experta + automatización eficaz

Limitar tiempo de vida de variables

Nuestros ethical hackers explican cómo evitar vulnerabilidades de seguridad mediante la creación, manipulación y eliminación correcta de variables u objetos dentro de un programa Java, evitando que información disponible en memoria pueda ser capturada por usuarios no autorizados.

Necesidad

Eliminar (limpiar) datos, variables y/o objetos creados por la aplicación cuando ya no estén en uso.

Contexto

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

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

  2. El código debe eliminar información sensible en memoria.

  3. La aplicación debe limpiar/eliminar datos, variables y/o objetos creados por la misma cuando ya no estén en uso,[1] limitando así su tiempo de vida.

Solución

  • Lectura no segura de credenciales:

    A continuación se describe un código de ejemplo en Java, el cual permite leer las credenciales de un usuario desde la consola:

    1. Se declara la clase Password y dentro de ella se define el método Main:

      password.java
      1
      2
        class Password {
          public static void main (String args[]) throws IOException {
      
    2. Se crea un objeto de la clase Console mediante el método estático console de la clase System:

      1
        Console c = System.console();
      
    3. Una vez creado dicho objeto, se verifica si este es nulo, es caso de serlo, se detiene la ejecución del programa mediante el método estático exit:

      1
      2
      3
      4
        if (c == null) {
          System.err.println("No console.");
          System.exit(1);
        }
      

      El valor de 1 dentro de dicho método se utiliza para indicar que la ejecución del programa falló, 0 en caso de ejecución exitosa.

    4. Se lee la información del nombre de usuario y la contraseña desde la consola mediante el método readLine de la clase Console. Las cadenas retornadas por dicho método se almacenan como objetos de tipo String:

      1
      2
        String username = c.readLine("Ingresa tu nombre de usuario: ");
        String password = c.readLine("Ingresa tu contraseña: ");
      
    5. Mediante el método verify que recibe el nombre usuario y contraseña, simulamos el proceso de validación del usuario (el lector debe implementar este método de acuerdo a sus necesidades):

       1
       2
       3
       4
       5
       6
       7
       8
       9
      10
      11
          if (!verify(username, password)) {
            throw new SecurityException("Invalid Credentials");
          }
      
        }
      
        // Validación simulada, siempre devuelve true
        private static final boolean verify(String username, String password) {
          return true;
        }
      }
      

      En el anterior bloque de código las credenciales permanecen expuestas hasta que el recolector de basura recupera la memoria asociada a dichas cadenas.

  • Lectura segura de credenciales:

    1. Para dar solución a lo anterior, se modificó el bloque de código de tal forma que se utilizó en primera medida el método readPassword (el cual retorna un array de caracteres) para obtener la contraseña desde la consola sin imprimir los caracteres en pantalla:

       1
       2
       3
       4
       5
       6
       7
       8
       9
      10
      11
      class Password {
        public static void main (String args[]) throws IOException {
          Console c = System.console();
      
          if (c == null) {
            System.err.println("No console.");
            System.exit(1);
          }
      
          String username = c.readLine("Enter your user name: ");
          char[] password = c.readPassword("Enter your password: ");
      

      El método readPassword() permite que la contraseña sea retornada como una secuencia de caracteres en lugar de como un objeto String. Por lo tanto, no sobrevivirá a la recolección de elementos no utilizados, incluso si coincide con otra cadena. En consecuencia, el programador puede borrar la contraseña inmediatamente después de su uso.

    2. En segunda medida, se sobrescriben o limpian los datos mediante el método estático fill de la clase Arrays:

       1
       2
       3
       4
       5
       6
       7
       8
       9
      10
      11
      12
      13
      14
      15
      16
          boolean isValidUser = verify(username, password);
      
          // limpiar la contraseña
          Arrays.fill(password,' ');
      
          if (!isValidUser) {
            throw new SecurityException("Invalid Credentials");
          }
      
        }
      
        // Validación simulada, siempre devuelve true
        private static final boolean verify(String username, char[] password) {
          return true;
        }
      }
      
    3. Para ejecutar el anterior programa, desde la terminal se debe ubicar primero en el directorio del proyecto y ejecutar el comando javac:

      1
      2
      3
      4
      5
      $ ls
      Password.java
      $ javac Password.java
      $ ls
      Password.java Password.class
      
    4. Luego de compilar el programa correctamente, se obtiene el bytecode (archivo .class) del programa y se procede a ejecutar el mismo mediante el comando Java:

      1
      $ java Password.class
      
    5. La salida en consola luego de ejecutar el programa es la siguiente (los símbolos * son simbólicos ya que en pantalla no se verá nada):

      1
      2
      Ingresa tu nombre de usuario: FLUIDtest
      Ingresa tu contraseña: *******
      

Descargas

Puedes descargar el código fuente pulsando en el siguiente enlace:

  1. Password.java contiene todas las instrucciones Java para el manejo de credenciales de manera segura.




Haz un comentario

Estado de los servicios - Términos de Uso