¿Por qué se prefiere char [] a String para contraseñas?


En Swing, el campo password tiene un método getPassword() (returns char[]) en lugar del método habitual getText() (returns String). Del mismo modo, me he encontrado con una sugerencia de no usar String para manejar contraseñas.

¿Por qué String representa una amenaza para la seguridad cuando se trata de contraseñas? Se siente incómodo usar char[].

Author: Rakete1111, 2012-01-16

17 answers

Las Cadenas son inmutables. Eso significa que una vez que haya creado el String, si otro proceso puede volcar la memoria, no hay manera (aparte de reflexión) puede deshacerse de los datos antes de recolección de basura entra en acción.

Con una matriz, puede borrar explícitamente los datos una vez que haya terminado con ella. Puede sobrescribir la matriz con cualquier cosa que desee, y la contraseña no estará presente en ningún lugar del sistema, incluso antes de la recolección de basura.

Así que sí, esto es una preocupación de seguridad, pero incluso usar char[] solo reduce la ventana de oportunidad para un atacante, y es solo para este tipo específico de ataque.

Como se ha señalado en los comentarios, es posible que los arrays movidos por el recolector de basura dejen copias perdidas de los datos en la memoria. Creo que esto es específico de la implementación: el recolector de basura puede borrar toda la memoria a medida que avanza, para evitar este tipo de cosas. Incluso si lo hace, todavía hay tiempo durante que el char[] contiene los caracteres reales como una ventana de ataque.

 3795
Author: Jon Skeet,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-04-20 17:50:27

Mientras que otras sugerencias aquí parecen válidas, hay otra buena razón. Con plain String tiene muchas más posibilidades de que imprima accidentalmente la contraseña en los registros, monitores u otro lugar inseguro. char[] es menos vulnerable.

Considere esto:

public static void main(String[] args) {
    Object pw = "Password";
    System.out.println("String: " + pw);

    pw = "Password".toCharArray();
    System.out.println("Array: " + pw);
}

Impresiones:

String: Password
Array: [C@5829428e
 1102
Author: Konrad Garus,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-01-19 18:11:54

Para citar un documento oficial, la guía de Arquitectura de Criptografía de Java dice esto sobre char[] vs. String contraseñas (sobre el cifrado basado en contraseñas, pero esto es más generalmente sobre contraseñas, por supuesto):

Parece lógico recopilar y almacenar la contraseña en un objeto de tipo java.lang.String. Sin embargo, aquí está la advertencia: Object s de tipo String son inmutables, es decir, no hay métodos definidos que permite cambiar (sobrescribir) o poner a cero el contenido de un String después del uso. Esta característica hace que los objetos String no sean adecuados para almacenamiento de información confidencial de seguridad, como contraseñas de usuario. Usted siempre debe recopilar y almacenar información confidencial de seguridad en un char array en su lugar.

La Directriz 2-2 de las Pautas de Codificación Segura para el Lenguaje de Programación Java, Versión 4.0 también dice algo similar (aunque originalmente está en el contexto del registro):

Directriz 2-2: No registrar información sensible

Cierta información, como los números de Seguro Social (SSN) y contraseñas, es altamente sensible. Esta información no debe conservarse por más tiempo del necesario ni donde pueda ser visto, incluso por administrador. Por ejemplo, no se debe enviar a los archivos de registro y su presencia no debe ser detectable mediante registros. Algunos transitorios los datos pueden mantenerse en estructuras de datos mutables, como matrices char, y se elimina inmediatamente después de su uso. Compensación las estructuras de datos se han reducido efectividad en sistemas de ejecución Java típicos a medida que se mueven objetos memoria transparente para el programador.

Esta directriz también tiene implicaciones para la implementación y el uso de bibliotecas de nivel inferior que no tienen conocimiento semántico de los datos están tratando. Como ejemplo, un análisis de cadena de bajo nivel la biblioteca puede registrar el texto en el que trabaja. Una aplicación puede analizar un SSN con la biblioteca. Esto crea una situación en la que el SSNS ser disponible para los administradores con acceso a los archivos de registro.

 625
Author: Bruno,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-01-17 03:20:57

Las matrices de caracteres (char[]) se pueden borrar después de su uso estableciendo cada carácter a cero y las cadenas no. Si alguien puede ver de alguna manera la imagen de memoria, puede ver una contraseña en texto plano si se usan cadenas, pero si se usa char[], después de purgar los datos con 0, la contraseña es segura.

 311
Author: alephx,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-03-10 17:50:52

Algunas personas creen que tienes que sobrescribir la memoria utilizada para almacenar la contraseña una vez que ya no la necesitas. Esto reduce la ventana de tiempo que un atacante tiene para leer la contraseña de su sistema e ignora completamente el hecho de que el atacante ya necesita suficiente acceso para secuestrar la memoria JVM para hacer esto. Un atacante con tanto acceso puede atrapar tus eventos clave haciendo que esto sea completamente inútil (AFAIK, así que corrígeme si lo soy mal).

Update

Gracias a los comentarios tengo que actualizar mi respuesta. Al parecer, hay dos casos en los que esto puede agregar una (muy) menor mejora de la seguridad, ya que reduce el tiempo que una contraseña podría aterrizar en el disco duro. Todavía creo que es excesivo para la mayoría de los casos de uso.

  • Su sistema de destino puede estar mal configurado o debe asumir que lo está y debe estar paranoico con los volcados de núcleo (puede ser válido si los sistemas no son administrados por un administrador).
  • Tu software tiene que ser demasiado paranoico para evitar fugas de datos con el atacante obteniendo acceso al hardware - usando cosas como TrueCrypt (descontinuado), VeraCrypt, o CipherShed.

Si es posible, deshabilitar los volcados de núcleo y el archivo swap se encargaría de ambos problemas. Sin embargo, requerirían derechos de administrador y podrían reducir la funcionalidad (menos memoria para usar) y extraer RAM de un sistema en ejecución seguiría siendo válido preocupación.

 187
Author: josefx,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-07-19 22:52:01
  1. Las cadenas son inmutables en Java si almacena la contraseña como texto sin formato, estará disponible en la memoria hasta que el recolector de basura la borre y, dado que las cadenas se utilizan en el grupo de cadenas para su reutilización, existe una probabilidad bastante alta de que permanezca en la memoria durante mucho tiempo, lo que representa una amenaza para la seguridad. Dado que cualquier persona que tenga acceso al volcado de memoria puede encontrar la contraseña en clear text
  2. Recomendación Java usando getPassword() método de JPasswordField que devuelve un método char[] y deprecated getText() que devuelve la contraseña en texto claro indicando la razón de seguridad.
  3. ToString () siempre existe el riesgo de imprimir texto plano en un archivo de registro o consola, pero si usa Array, no imprimirá el contenido del array, sino que se imprimirá su ubicación de memoria.

    String strPwd = "passwd";
    char[] charPwd = new char[]{'p','a','s','s','w','d'};
    System.out.println("String password: " + strPwd );
    System.out.println("Character password: " + charPwd );
    

    String passwd{[16]]}

    Contraseña de carácter: [C@110b2345

Pensamientos finales: Aunque usar char [] no es solo lo suficiente para borrar el contenido para ser más seguro. También sugiero trabajar con contraseña cifrada o con hash en lugar de texto plano y borrarla de la memoria tan pronto como se complete la autenticación.

 121
Author: Srujan Kumar Gulla,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-06-06 08:26:12

No creo que esta sea una sugerencia válida, pero, al menos puedo adivinar la razón.

Creo que la motivación es querer asegurarse de que puede borrar todo rastro de la contraseña en la memoria rápidamente y con certeza después de que se utiliza. Con un char[] podría sobrescribir cada elemento de la matriz con un espacio en blanco o algo seguro. No se puede editar el valor interno de un String de esa manera.

Pero eso por sí solo no es una buena respuesta; ¿por qué no asegurarse de que una referencia a la char[] o String no se escapa? Entonces no hay problema de seguridad. Pero la cosa es que String los objetos pueden ser intern() ed en teoría y mantenerse vivos dentro de la piscina constante. Supongo que usar char[] prohíbe esta posibilidad.

 74
Author: Sean Owen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-01-16 14:27:54

La respuesta ya se ha dado, pero me gustaría compartir un problema que descubrí últimamente con las bibliotecas estándar de Java. Si bien ahora tienen mucho cuidado de reemplazar las cadenas de contraseñas con char[] en todas partes (lo que, por supuesto, es una buena cosa), otros datos críticos para la seguridad parecen pasarse por alto cuando se trata de borrarlos de la memoria.

Estoy pensando, por ejemplo, en la clase PrivateKey. Considere un escenario en el que cargaría una clave RSA privada desde un archivo PKCS # 12, usándola para realice alguna operación. Ahora, en este caso, oler la contraseña por sí sola no le ayudaría mucho, siempre y cuando el acceso físico al archivo de clave esté correctamente restringido. Como atacante, estaría mucho mejor si obtuviera la clave directamente en lugar de la contraseña. La información deseada puede ser múltiple filtrado, volcados de núcleo, una sesión de depurador o archivos de intercambio son solo algunos ejemplos.

Y resulta que no hay nada que te permita borrar la información privada de un PrivateKey de la memoria, porque no hay ninguna API que te permita borrar los bytes que forman la información correspondiente.

Esta es una mala situación, ya que este documento describe cómo esta circunstancia podría ser potencialmente explotada.

La biblioteca OpenSSL, por ejemplo, sobrescribe secciones de memoria críticas antes de liberar claves privadas. Dado que Java se recopila basura, necesitaríamos métodos explícitos para borrar e invalidar la información privada de las claves de Java, que se aplicarán inmediatamente después de usar clave.

 60
Author: emboss,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-01-19 19:39:47

Como dice Jon Skeet, no hay otra manera que usar la reflexión.

Sin embargo, si la reflexión es una opción para usted, puede hacer esto.

public static void main(String[] args) {
    System.out.println("please enter a password");
    // don't actually do this, this is an example only.
    Scanner in = new Scanner(System.in);
    String password = in.nextLine();
    usePassword(password);

    clearString(password);

    System.out.println("password: '" + password + "'");
}

private static void usePassword(String password) {

}

private static void clearString(String password) {
    try {
        Field value = String.class.getDeclaredField("value");
        value.setAccessible(true);
        char[] chars = (char[]) value.get(password);
        Arrays.fill(chars, '*');
    } catch (Exception e) {
        throw new AssertionError(e);
    }
}

Cuando se ejecuta

please enter a password
hello world
password: '***********'

Nota: si el char[] de la cadena se ha copiado como parte de un ciclo GC, existe la posibilidad de que la copia anterior esté en algún lugar de la memoria.

Esta copia antigua no aparecería en un volcado de pilas, pero si tiene acceso directo a la memoria raw del proceso, podría verla. En general, debe evitar a cualquier persona tener tal acceso.

 42
Author: Peter Lawrey,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-07-08 09:34:03

Edit: Volviendo a esta respuesta después de un año de investigación de seguridad, me doy cuenta de que hace la implicación bastante desafortunada de que alguna vez compararía contraseñas de texto plano. Por favor, no. Use un hash unidireccional seguro con una sal y un número razonable de iteraciones. Considere usar una biblioteca: ¡esto es difícil de hacer bien!

Respuesta original: ¿Qué pasa con el hecho de que la cadena.equals () usa evaluación de cortocircuito, y por lo tanto es ¿vulnerable a un ataque de sincronización? Puede ser poco probable, pero podría teóricamente programar la comparación de contraseñas para determinar la secuencia correcta de caracteres.

public boolean equals(Object anObject) {
    if (this == anObject) {
        return true;
    }
    if (anObject instanceof String) {
        String anotherString = (String)anObject;
        int n = value.length;
        // Quits here if Strings are different lengths.
        if (n == anotherString.value.length) {
            char v1[] = value;
            char v2[] = anotherString.value;
            int i = 0;
            // Quits here at first different character.
            while (n-- != 0) {
                if (v1[i] != v2[i])
                    return false;
                i++;
            }
            return true;
        }
    }
    return false;
}

Algunos recursos más sobre ataques de tiempo:

 34
Author: Graph Theory,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:26:29

Estas son todas las razones, uno debe elegir char [] array en lugar de String para la contraseña.

1. Dado que las cadenas son inmutables en Java si almacena la contraseña como texto sin formato, estará disponible en la memoria hasta que el recolector de basura lo borre y dado que las cadenas se usan en el grupo de cadenas para su reutilización, hay una probabilidad bastante alta de que permanezca en la memoria durante mucho tiempo, lo que representa una amenaza para la seguridad. Ya que cualquiera que tenga acceso al volcado de memoria puede encontrar la contraseña en texto claro y esa es otra razón por la que siempre debe usar una contraseña cifrada que el texto sin formato. Dado que las cadenas son inmutables, no hay forma de cambiar el contenido de las Cadenas porque cualquier cambio producirá una nueva Cadena, mientras que si char [] todavía puede establecer todo su elemento como en blanco o cero. Por lo tanto, almacenar la contraseña en una matriz de caracteres mitiga claramente el riesgo de seguridad de robar la contraseña.

2. Java recomienda usar el método getPassword() de JPasswordField que devuelve un método char[] y getText() obsoleto que devuelve la contraseña en texto claro indicando la razón de seguridad. Es bueno seguir los consejos del equipo de Java y adherirse a la norma en lugar de ir en contra de ella.

3. Con Cadena siempre hay un riesgo de imprimir texto sin formato en el archivo de registro o consola, pero si usa Matriz no imprimirá el contenido de la matriz en lugar de su ubicación de memoria se imprimirá. aunque no es una razón real, pero todavía tiene sentido.

    String strPassword="Unknown";
    char[] charPassword= new char[]{'U','n','k','w','o','n'};
    System.out.println("String password: " + strPassword);
    System.out.println("Character password: " + charPassword);

    String password: Unknown
    Character password: [C@110b053

Referencia de: http://javarevisited.blogspot.com/2012/03/why-character-array-is-better-than.html Espero que esto ayude.

 34
Author: Human Being,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-11-04 08:13:42

No hay nada que char array le da vs Cadena a menos que limpie manualmente después de su uso, y no he visto a nadie realmente haciendo eso. Así que para mí la preferencia de char[] vs Cadena es un poco exagerado.

Echa un vistazo a la ampliamente utilizada Spring Security library aquí y pregúntate a ti mismo: ¿son incompetentes los chicos de Spring Security o las contraseñas char [] simplemente no tienen mucho sentido? Cuando algún hacker desagradable agarra los vertederos de memoria de su RAM asegúrese de que obtendrá todos los contraseñas incluso si utilizas formas sofisticadas de ocultarlas.

Sin embargo, Java cambia todo el tiempo, y algunas características aterradoras como la función de Deduplicación de cadenas de Java 8 podrían internar objetos de cadenas sin su conocimiento. Pero esa es una conversación diferente.

 31
Author: Oleg Mikheev,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:18:26

Las cadenas son inmutables y no se pueden alterar una vez creadas. Crear una contraseña como una cadena dejará referencias perdidas a la contraseña en el montón o en el grupo de cadenas. Ahora, si alguien toma un volcado del proceso Java y analiza cuidadosamente a través de él podría ser capaz de adivinar las contraseñas. Por supuesto, estas cadenas no usadas serán basura recolectada, pero eso depende de cuando el GC se active.

En el otro lado char [] son mutables tan pronto como la autenticación es hecho puede sobrescribirlos con cualquier carácter como todas las M o barras invertidas. Ahora, incluso si alguien toma un volcado de pilas, es posible que no pueda obtener las contraseñas que no están actualmente en uso. Esto le da más control en el sentido de limpiar el contenido del Objeto usted mismo en lugar de esperar a que el GC lo haga.

 28
Author: Geek,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-07-28 10:46:40

La respuesta corta y directa sería porque char[] es mutable mientras que String los objetos no lo son.

Strings en Java son objetos inmutables. Es por eso que no se pueden modificar una vez creados, y por lo tanto la única manera de que sus contenidos se eliminen de la memoria es que se recopilen basura. Será solo entonces cuando la memoria liberada por el objeto pueda ser sobrescrita, y los datos desaparecerán.

Ahora la recolección de basura en Java no sucede en ningún garantizado intervalo. Por lo tanto, el String puede persistir en la memoria durante mucho tiempo, y si un proceso falla durante este tiempo, el contenido de la cadena puede terminar en un volcado de memoria o en algún registro.

Con una matriz de caracteres , puede leer la contraseña, terminar de trabajar con ella tan pronto como pueda y luego cambiar inmediatamente el contenido.

 16
Author: Pritam Banerjee,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-01-05 23:40:12

1) Dado que las cadenas son inmutables en Java si almacena la contraseña como texto sin formato, estará disponible en memoria hasta que el recolector de basura la borre y dado que la cadena se utiliza en el grupo de cadenas para su reutilización, existe una probabilidad bastante alta de que permanezca en memoria durante un largo período, lo que representa una amenaza para la seguridad. Dado que cualquier persona que tenga acceso al volcado de memoria puede encontrar la contraseña en texto claro y esa es otra razón por la que siempre debe usar una contraseña cifrada que el texto sin formato. Ya Las cadenas son inmutables, no hay forma de que el contenido de las Cadenas se pueda cambiar porque cualquier cambio producirá una nueva Cadena, mientras que si char [] todavía puede establecer todo su elemento como en blanco o cero. Por lo tanto, almacenar la contraseña en una matriz de caracteres mitiga el riesgo de seguridad de robar la contraseña.

2) Java recomienda usar el método getPassword() de JPasswordField que devuelve un método char [] y getText() obsoleto que devuelve la contraseña en texto claro indicando la razón de seguridad. Es bueno seguir consejo del equipo de Java y adherirse a la norma en lugar de ir en contra de ella.

 14
Author: Neeraj Gahlawat,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-01-08 20:42:18

La cadena en java es inmutable. Así que cada vez que se crea una cadena, permanecerá en la memoria hasta que se recolecte basura. Así que cualquiera que tenga acceso a la memoria puede leer el valor de la cadena.
Si se modifica el valor de la cadena, terminará creando una nueva cadena. Así que tanto el valor original como el valor modificado permanecen en la memoria hasta que se recolecta basura.

Con la matriz de caracteres, el contenido de la matriz se puede modificar o borrar una vez que el propósito de la contraseña se sirve. El contenido original de la matriz no se encontrará en la memoria después de que se modifique e incluso antes de que comience la recolección de basura.

Debido a la preocupación de seguridad, es mejor almacenar la contraseña como una matriz de caracteres.

 11
Author: Saathvik,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-07-28 06:33:06

La cadena es inmutable y va al grupo de cadenas. Una vez escrito, no se puede sobrescribir.

char[] es un array que debes sobrescribir una vez que usaste la contraseña y así es como se debe hacer:

char[] passw = request.getPassword().toCharArray()
if (comparePasswords(dbPassword, passw) {
 allowUser = true;
 cleanPassword(passw);
 cleanPassword(dbPassword);
 passw=null;
}

private static void cleanPassword (char[] pass) {
 for (char ch: pass) {
  ch = null;
 }
}

Un escenario donde el atacante podría usarlo es un crashdump - cuando la JVM se bloquea y genera un volcado de memoria - usted será capaz de ver la contraseña.

Eso no es necesariamente un atacante externo malicioso. Esto podría ser un usuario de soporte que tiene acceso al servidor para fines de monitoreo. Podría echar un vistazo a un crashdump y encontrar las contraseñas.

 10
Author: ACV,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-04-25 22:39:55