Las cuerdas son inmutables . Eso significa que una vez que haya creado el String
, si otro proceso puede volcar la memoria, no hay forma (aparte de la reflexión ) de que pueda deshacerse de los datos antes de que comience la recolección de basura .
Con una matriz, puede borrar explícitamente los datos una vez que haya terminado con ella. Puede sobrescribir la matriz con lo que desee, y la contraseña no estará presente en ninguna parte del sistema, incluso antes de la recolección de basura.
Entonces, sí, esto es un problema de seguridad, pero incluso el uso 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 que mueve 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 puede borrar toda la memoria a medida que avanza, para evitar este tipo de cosas. Incluso si lo hace, todavía hay el tiempo durante el cual char[]
contiene los personajes reales como una ventana de ataque.
char[]
ubicaciones corta esa línea de ataque, algo que no es posible cuando se usaString
.Si bien otras sugerencias aquí parecen válidas, hay otra buena razón. Con plain
String
tiene muchas más posibilidades de imprimir accidentalmente la contraseña en registros , monitores u otro lugar inseguro.char[]
Es menos vulnerable.Considera esto:
Huellas dactilares:
fuente
toString
esclassname@hashcode
.[C
representachar[]
, el resto es código hash hexadecimal.Password
tipo de clase para esto. Es menos oscuro y más difícil pasar accidentalmente a algún lado.Para citar un documento oficial, la guía de Java Cryptography Architecture dice lo siguiente acerca
char[]
vs.String
contraseñas (sobre la encriptación basada en contraseña, pero esto es más general acerca de las contraseñas, por supuesto):La 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):
fuente
String
se entiende bastante bien y cómo se comporta en la JVM ... hay buenas razones para usarchar[]
en lugar deString
cuando se trata de contraseñas de manera segura.At which point
es una aplicación específica, pero la regla general es hacerlo tan pronto como obtenga algo que se supone que es una contraseña (texto sin formato o de otro modo). Lo obtiene del navegador como parte de una solicitud HTTP, por ejemplo. No puede controlar la entrega, pero puede controlar su propio almacenamiento, así que tan pronto como lo reciba, colóquelo en un char [], haga lo que necesite hacer con él, luego configure todo en '0' y deje que el gc reclamarlo.Las matrices de caracteres (
char[]
) se pueden borrar después del uso al establecer cada carácter en cero y las cadenas no. Si alguien puede ver de alguna manera la imagen de la memoria, puede ver una contraseña en texto sin formato si se usan cadenas, pero sichar[]
se usa, después de purgar los datos con 0, la contraseña es segura.fuente
HttpServletRequest
objeto en texto sin formato. Si la versión de JVM es 1.6 o inferior, estará en el espacio permgen. Si está en 1.7, seguirá siendo legible hasta que se recoja. (Siempre que sea así)intern()
a cadenas arbitrarias sin pensar . Pero tiene razón en que lasString
instancias existen en primer lugar (hasta que se recopilan) y convertirlas enchar[]
matrices después no lo cambia.new String()
oStringBuilder.toString()
administré aplicaciones con muchas constantes de cadena, y como resultado tuvimos una gran cantidad de permgen. Hasta 1.7.intern()
a una cadena arbitraria podría causar la asignación de una cadena equivalente en el espacio permgen. Este último podría obtener GC'ed, si no hubiera una cadena literal de los mismos contenidos compartiendo ese objeto ...Algunas personas creen que tiene que sobrescribir la memoria utilizada para almacenar la contraseña una vez que ya no la necesita. Esto reduce el 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 acceso suficiente para secuestrar la memoria JVM para hacer esto. Un atacante con tanto acceso puede atrapar tus eventos clave y hacer que esto sea completamente inútil (AFAIK, así que corrígeme si me equivoco).
Actualizar
Gracias a los comentarios tengo que actualizar mi respuesta. Aparentemente, hay dos casos en los que esto puede agregar una mejora de seguridad (muy) menor, ya que reduce el tiempo que una contraseña podría aterrizar en el disco duro. Aún así, creo que es excesivo para la mayoría de los casos de uso.
Si es posible, deshabilitar los volcados del núcleo y el archivo de intercambio se encargará 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 una preocupación válida.
fuente
No creo que sea una sugerencia válida, pero al menos puedo adivinar el motivo.
Creo que la motivación es querer asegurarse de que puede borrar todo rastro de la contraseña en la memoria de manera rápida y segura después de su uso. 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 deString
esa manera.Pero eso solo no es una buena respuesta; ¿por qué no simplemente asegurarse de que no se escape una referencia al
char[]
oString
no? Entonces no hay problema de seguridad. Pero la cuestión es que losString
objetos pueden serintern()
editados en teoría y mantenerse vivos dentro del grupo constante. Supongo que usarchar[]
prohíbe esta posibilidad.fuente
char[]
pueden modificarse, y luego es irrelevante si se recopilan o no. Y dado que el internamiento de cadenas debe hacerse explícitamente para los no literales, es lo mismo que decir que unchar[]
campo estático puede hacer referencia a a.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ña por
char[]
todas partes (lo que, por supuesto, es algo bueno), otros datos críticos de 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, utilizándola para realizar alguna operación. Ahora, en este caso, solo rastrear la contraseña no lo ayudaría siempre y cuando el acceso físico al archivo de clave esté restringido adecuadamente. Como atacante, estaría mucho mejor si obtuviera la clave directamente en lugar de la contraseña. La información deseada puede ser filtrada, volcados de núcleo, una sesión de depurador o archivos de intercambio son solo algunos ejemplos.
Y resulta que no hay nada que le permita borrar la información privada de a
PrivateKey
de la memoria, porque no hay API que le 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 las claves privadas. Como Java se recolecta basura, necesitaríamos métodos explícitos para borrar e invalidar la información privada de las claves Java, que se aplicarán inmediatamente después de usar la clave.
fuente
PrivateKey
que en realidad no cargue su contenido privado en la memoria: a través de un token de hardware PKCS # 11, por ejemplo. Quizás una implementación de software de PKCS # 11 podría encargarse de limpiar la memoria manualmente. Quizás sea mejor usar algo como la tienda NSS (que comparte la mayor parte de su implementación con elPKCS11
tipo de tienda en Java). ElKeychainStore
(almacén de claves OSX) carga el contenido completo de la clave privada en susPrivateKey
instancias, pero no debería ser necesario. (No estoy seguro de lo queWINDOWS-MY
hace KeyStore en Windows).Como dice Jon Skeet, no hay manera, excepto mediante el uso de la reflexión.
Sin embargo, si la reflexión es una opción para usted, puede hacerlo.
cuando corre
Nota: si el carácter de String [] 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 antigua no aparecería en un volcado de almacenamiento dinámico, pero si tiene acceso directo a la memoria sin procesar del proceso, podría verla. En general, debe evitar que alguien tenga dicho acceso.
fuente
'***********'
.Scanner
el búfer interno y, como no la usóSystem.console().readPassword()
, en forma legible en la ventana de la consola. Pero para la mayoría de los casos de uso práctico, la duración deusePassword
la ejecución es el problema real. Por ejemplo, cuando se conecta a otra máquina, lleva un tiempo considerable y le dice al atacante que es el momento adecuado para buscar la contraseña en el montón. La única solución es evitar que los atacantes lean la memoria del montón ...Estas son todas las razones, uno debe elegir una matriz char [] en lugar de String para una contraseña.
1. Dado que los Strings 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 String se usa en el conjunto de String para su reutilización, existe una posibilidad bastante alta de que lo haga permanecer en la memoria durante un período prolongado, lo que representa una amenaza para la seguridad.
Dado que cualquiera que tenga acceso al volcado de memoria puede encontrar la contraseña en texto sin cifrar, esa es otra razón por la que siempre debe usar una contraseña encriptada en lugar de texto sin formato. Dado que 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 usa un carácter [] aún puede establecer todos los elementos en blanco o cero. Por lo tanto, almacenar una contraseña en una matriz de caracteres mitiga claramente el riesgo de seguridad de robar una contraseña.
2. Java mismo recomienda usar el método getPassword () de JPasswordField que devuelve un char [], en lugar del método obsoleto getText () que devuelve las contraseñas en texto claro indicando razones de seguridad. Es bueno seguir los consejos del equipo de Java y adherirse a los estándares en lugar de ir en contra de ellos.
3. Con String siempre existe el riesgo de imprimir texto sin formato en un archivo de registro o consola, pero si usa una matriz, no imprimirá el contenido de una matriz, sino que se imprimirá su ubicación de memoria. Aunque no es una razón real, todavía tiene sentido.
Referenciado desde este blog . Espero que esto ayude.
fuente
Editar: Volviendo a esta respuesta después de un año de investigación de seguridad, me doy cuenta de que tiene la desafortunada implicación de que realmente compararía las contraseñas de texto sin formato. Por favor no lo hagas. Use un hash unidireccional seguro 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 la evaluación de cortocircuito y, por lo tanto, es vulnerable a un ataque de sincronización? Puede ser poco probable, pero teóricamente podría cronometrar la comparación de contraseña para determinar la secuencia correcta de caracteres.
Algunos recursos más sobre ataques de tiempo:
fuente
Arrays.equals
parachar[]
es tan alto como paraString.equals
. Si a alguien le importaba, había una clase de clave dedicada que encapsulaba la contraseña real y se ocupaba de los problemas: oh, espera, los paquetes de seguridad reales tienen clases de clave dedicadas, estas preguntas y respuestas son solo un hábito fuera de ellas, por ejemploJPasswordField
, para usarchar[]
en lugar deString
(donde los algoritmos reales usan debyte[]
todos modos).sleep(secureRandom.nextInt())
antes de rechazar un intento de inicio de sesión de todos modos, eso no solo elimina la posibilidad de ataques de tiempo, sino que también contrarresta los intentos de fuerza bruta.No hay nada que le dé char array frente a String a menos que lo limpie manualmente después de su uso, y no he visto a nadie realmente haciendo eso. Entonces, para mí, la preferencia de char [] frente a String es un poco exagerada.
Eche un vistazo a la biblioteca Spring Security ampliamente utilizada aquí y pregúntese: ¿son incompetentes los tipos de Spring Security o las contraseñas char [] simplemente no tienen mucho sentido? Cuando un pirata informático desagradable toma volcados de memoria de su RAM, asegúrese de obtener todas las contraseñas, incluso si utiliza formas sofisticadas para 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 pueden internar objetos de cadenas sin su conocimiento. Pero esa es una conversación diferente.
fuente
Las cadenas son inmutables y no se pueden modificar una vez que se han creado. Crear una contraseña como una cadena dejará referencias parciales a la contraseña en el montón o en el conjunto de cadenas. Ahora, si alguien realiza un volcado del proceso Java y lo escanea cuidadosamente, podría adivinar las contraseñas. Por supuesto, estas cadenas no utilizadas se recogerán como basura, pero eso depende de cuándo se active el GC.
Por otro lado, char [] son mutables tan pronto como se realiza la autenticación, puede sobrescribirlos con cualquier carácter como todas las M o barras invertidas. Ahora, incluso si alguien realiza un volcado de almacenamiento dinámico, 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 borrar el contenido del Objeto usted mismo frente a esperar a que el GC lo haga.
fuente
strings
Y elString
grupo (grupo de cadenas usadas previamente) AMBOS se almacenaron en Permgen antes de 1.7. Además, consulte la sección 5.1: docs.oracle.com/javase/specs/jvms/se6/html/… La JVM siempre verificóStrings
si tenían el mismo valor de referencia y lo llamaríaString.intern()
POR USTED. El resultado fue que cada vez que la JVM detectaba cadenas idénticas en elconstant_pool
o montón las movía a permgen. Y trabajé en varias aplicaciones con "permgen progresivo" hasta 1.7. Fue un verdadero problema.constant_pool
permgen ubicado en IN, y luego, si se usaba una cadena más de una vez, sería internada.La cadena es inmutable y va al grupo de cadenas. Una vez escrito, no se puede sobrescribir.
char[]
es una matriz que debe sobrescribir una vez que haya usado la contraseña y así es como debe hacerse:Un escenario en el que el atacante podría usarlo es un crashdump: cuando la JVM se bloquea y genera un volcado de memoria, podrá ver la contraseña.
Eso no es necesariamente un atacante externo malicioso. Este podría ser un usuario de soporte que tiene acceso al servidor para fines de monitoreo. Podía echar un vistazo a una caída de emergencia y encontrar las contraseñas.
fuente
request.getPassword()
ya no crea la cadena y la agrega al grupo?ch = '0'
cambia la variable localch
; no tiene efecto en la matriz. Y su ejemplo no tiene sentido de todos modos, comienza con una instancia de cadena a la que llamatoCharArray()
, creando una nueva matriz e incluso cuando sobrescribe la nueva matriz correctamente, no cambia la instancia de cadena, por lo que no tiene ventaja sobre solo usar la cadena ejemplo.Arrays.fill(pass, '0');
La respuesta corta y directa sería porque
char[]
es mutable mientras que losString
objetos no lo son.Strings
En Java son objetos inmutables. Es por eso que no pueden modificarse una vez creados, y por lo tanto, la única forma de eliminar su contenido de la memoria es recolectar basura. Será solo entonces cuando la memoria liberada por el objeto pueda sobrescribirse y los datos se habrán ido.Ahora la recolección de basura en Java no ocurre en ningún intervalo garantizado. Por
String
lo tanto, puede permanecer en la memoria durante mucho tiempo, y si un proceso se bloquea durante este tiempo, el contenido de la cadena puede terminar en un volcado de memoria o algún registro.Con una matriz de caracteres , puede leer la contraseña, terminar de trabajar con ella tan pronto como sea posible y luego cambiar de inmediato el contenido.
fuente
La cadena en Java es inmutable. Por lo tanto, cada vez que se crea una cadena, permanecerá en la memoria hasta que se recolecte basura. Por lo tanto, 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. Por lo tanto, 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 se cumple el propósito de la contraseña. 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.
fuente
Es discutible si debe usar String o Char [] para este propósito porque ambos tienen sus ventajas y desventajas. Depende de lo que el usuario necesite.
Dado que las cadenas en Java son inmutables, cada vez que algunos intentan manipular su cadena, crea un nuevo objeto y la cadena existente no se ve afectada. Esto podría verse como una ventaja para almacenar una contraseña como una cadena, pero el objeto permanece en la memoria incluso después de su uso. Entonces, si alguien obtuvo de alguna manera la ubicación de la memoria del objeto, esa persona puede rastrear fácilmente su contraseña almacenada en esa ubicación.
Char [] es mutable, pero tiene la ventaja de que después de su uso, el programador puede limpiar explícitamente la matriz o anular los valores. Entonces, cuando termina de usarse, se limpia y nadie podría saber sobre la información que había almacenado.
En base a las circunstancias anteriores, uno puede hacerse una idea de si ir con String o ir con Char [] para sus requisitos.
fuente
Cadena de caja:
Caso CHAR ARRAY:
fuente