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


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

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


2894
2018-01-16 14:20


origen


Respuestas:


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

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

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

Como se señaló en los comentarios, es posible que las matrices movidas por el recolector de basura dejarán copias perdidas de los datos en la memoria. Creo que esto es específico de la implementación: el recolector de basura mayo borre toda la memoria a medida que avanza, para evitar este tipo de cosas. Incluso si lo hace, todavía hay un tiempo durante el cual char[] contiene los personajes reales como una ventana de ataque.


3714
2018-01-16 14:26



Mientras que otras sugerencias aquí parecen válidas, hay otra buena razón. Con llanura String tienes muchas más posibilidades de accidentalmente imprimiendo la contraseña a los registros, monitores o algún otro lugar inseguro. char[] es menos vulnerable

Considera esto:

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

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

Huellas dactilares:

String: Password
Array: [[email protected]

1078
2018-01-16 19:37



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

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

Pauta 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 se encuentra en el contexto del registro):

Pauta 2-2: No registre información altamente confidencial

Parte de la información, como los números de Seguridad Social (SSN) y   contraseñas, es muy sensible. Esta información no debe mantenerse   por más tiempo de lo necesario ni donde pueda ser visto, incluso por   administradores. Por ejemplo, no se debe enviar a archivos de registro y   su presencia no debe ser detectable a través de búsquedas. Algunos transitorios   los datos se pueden guardar en estructuras de datos mutables, como matrices de caracteres, y   aclarado inmediatamente después del uso. La compensación de las estructuras de datos se ha reducido   efectividad en los sistemas de tiempo de ejecución Java típicos a medida que se mueven los objetos   memoria de forma transparente para el programador.

Esta guía 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 lidiando con. Como ejemplo, un análisis de cadenas 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 donde los SSN son   disponible para los administradores con acceso a los archivos de registro.


610
2018-01-17 03:20



Matrices de caracteres (char[]) se puede borrar después de su uso configurando cada carácter a cero y cadenas no. Si alguien puede ver la imagen de memoria de algún modo, puede ver una contraseña en texto sin formato si se utilizan cadenas, pero si char[] se utiliza, después de purgar los datos con 0, la contraseña es segura.


305
2018-01-16 14:27



Algunas personas creen que debe sobrescribir la memoria utilizada para almacenar la contraseña una vez que ya no la necesita. Esto reduce la ventana de tiempo que un atacante tiene que leer la contraseña de su sistema e ignora por completo 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 sus eventos clave haciendo que esto sea completamente inútil (AFAIK, así que por favor corríjanme si estoy equivocado).

Actualizar

Gracias a los comentarios tengo que actualizar mi respuesta. Aparentemente hay dos casos en los que esto puede agregar una (muy) pequeña mejora de seguridad ya que reduce el tiempo que una contraseña puede ingresar en el disco duro. Aún así, creo que es excesivo para la mayoría de los casos de uso.

  • Su sistema de destino puede estar mal configurado o debe suponer que lo es y debe ser paranoico con respecto a los volcados de núcleo (puede ser válido si los sistemas no son administrados por un administrador).
  • Su software tiene que ser demasiado paranoico para evitar filtraciones de datos con el acceso del atacante al hardware, usando cosas como TrueCrypt (interrumpido), VeraCrypt, o CipherShed.

Si es posible, deshabilitar los volcados del núcleo y el archivo de intercambio se encargarían 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 todavía sería una preocupación válida.


183
2018-01-16 14:32



  1. Las cadenas son inmutables en Java, si almacena la contraseña como texto sin formato, estará disponible en la memoria hasta que Garbage collector la borre y, dado que Strings se utiliza en el grupo de cadenas para volver a utilizarla, hay muchas posibilidades de que permanezca en la memoria durante un período prolongado, lo que plantea una amenaza a la seguridad. Dado que cualquiera que tenga acceso al volcado de memoria puede encontrar la contraseña en texto claro
  2. Recomendación de Java utilizando getPassword() método de JPasswordField que devuelve un char [] y obsoleto getText() método que devuelve la contraseña en texto claro que indica el motivo de seguridad.
  3. Encadenar() siempre existe el riesgo de imprimir texto sin formato en el archivo de registro o en la consola, pero si utiliza Array, no imprimirá el contenido de la matriz, sino que 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 );
    

    Contraseña de cadena: passwd

    Contraseña del personaje: [C @ 110b2345

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


117
2017-12-27 20:22



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

Creo que la motivación es asegurarse de que puede borrar todo rastro de la contraseña en la memoria de inmediato 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 puede editar el valor interno de un String de esa manera.

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


72
2018-01-16 14:27



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

Estoy pensando en, por ejemplo, el Llave privada clase. Considere un escenario en el que cargue una clave RSA privada de un archivo PKCS # 12, usándola para realizar alguna operación. Ahora, en este caso, olfatear la contraseña solo no te ayudaría mucho, siempre y cuando el acceso físico al archivo de clave esté restringido adecuadamente. Como atacante, estarías mucho mejor si obtuvieras la clave directamente en lugar de la contraseña. La información deseada se puede filtrar múltiples, volcados de núcleo, una sesión de depuración o archivos de intercambio son solo algunos ejemplos.

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

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

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


58
2018-01-19 19:39



Como dice Jon Skeet, no hay otra forma que usar el reflejo.

Sin embargo, si la reflexión es una opción para ti, puedes 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 corre

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

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

Esta copia anterior no aparecería en un volcado dinámico, pero si tiene acceso directo a la memoria bruta del proceso, podría verla. En general, debe evitar que cualquiera tenga tal acceso.


41
2017-07-08 09:26



Editar: Volviendo a esta respuesta después de un año de investigación de seguridad, me doy cuenta de que tiene la desafortunada consecuencia de que alguna vez compararía las contraseñas de texto sin formato. Por favor no. Use un hash seguro de una vía con sal y un número razonable de iteraciones. Considere usar una biblioteca: ¡esto es difícil de entender!

Respuesta original: ¿Qué pasa con el hecho de que String.equals () utiliza evaluación de cortocircuito, y por lo tanto, es vulnerable a un ataque de tiempo? Puede ser poco probable, pero podrías teóricamente cronometrar la comparación de contraseña 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 los ataques de sincronización:


32
2018-04-08 00:04



Estas son todas las razones, uno debe elegir carbonizarse[] matriz en lugar de Cuerda 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 Garbage collector lo borre y ya que String se utiliza en el grupo de cadenas para reutilizar, existe una gran posibilidad de que permanezca en la memoria por mucho tiempo. que representan una amenaza a la seguridad. Dado que cualquiera que tenga acceso al volcado de memoria puede encontrar la contraseña en texto libre y esa es otra razón por la que siempre debe usar una contraseña encriptada en lugar de texto sin formato. Como las cadenas son inmutables, no hay forma de que se puedan cambiar los contenidos de las cadenas, ya que cualquier cambio producirá una cadena nueva, mientras que si se usa una cadena [], se puede configurar todo en blanco o cero. Por lo tanto, almacenar la contraseña en una matriz de caracteres mitiga claramente el riesgo de robo de contraseña.

2.  La propia Java recomienda utilizar el método getPassword () de JPasswordField que devuelve un método char [] y obsoleto getText () que devuelve la contraseña en texto claro indicando el motivo de seguridad. Es bueno seguir los consejos del equipo de Java y seguir el estándar en lugar de ir en contra.

3.  Con String siempre existe el riesgo de imprimir texto sin formato en el archivo de registro o la consola, pero si usa Array, no imprimirá el contenido de la matriz, sino que se imprimirá su ubicación de memoria. aunque no es una razón real, pero aún 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: [[email protected]

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


32
2018-05-08 10:17