referencia por parametros objeto manejo eliminar cache borrar java security memory

objeto - parametros por referencia java



Seguridad de Java: ¿cómo borrar/poner a cero la memoria asociada con un objeto?(Y/o asegúrese de que sea la única instancia/copia de una variable en particular) (5)

Estoy en una discusión en el trabajo sobre cómo proteger la información confidencial (por ejemplo, contraseñas) almacenada en un programa Java. Según los requisitos de seguridad, la memoria que contiene información confidencial se borra, por ejemplo, estableciendo los valores de los bytes en todos los ceros. La preocupación es que un atacante pueda observar la memoria asociada con el proceso de la aplicación, por lo que queremos limitar tanto como sea posible la ventana de tiempo en que esa información sensible se mantiene. Anteriormente, los proyectos involucraban C ++, por lo que bastaba un memset ().

(Por cierto, el uso de memset () ha sido cuestionado porque se sabe que algunos compiladores optimizan su uso del binario resultante basándose en la suposición de que, dado que la memoria no se usa más adelante, no hay necesidad de ponerlo en cero. el primer lugar. Esta propaganda es un descargo de responsabilidad para aquellos que buscan "memset" y "clear memory", etc.

Ahora tenemos en nuestras manos un proyecto de Java presionado contra este requisito.

Para objetos Java, mi entendimiento es que:

  • una referencia anulada solo cambia el valor de la referencia; la memoria en el montón para el objeto aún contiene datos
  • un objeto inmutable como String no podría tener sus datos modificados (o al menos no fácilmente, dentro de los confines de una máquina virtual con un administrador de seguridad debidamente habilitado)
  • los recolectores de basuras generacionales pueden hacer copias de objetos por todas partes (como se indica here )

Y para los primitivos, mi entendimiento es que:

  • una variable de tipo primitivo en un método local se asignaría en la pila, y:
  • cuando cambia su valor, lo modifica directamente en la memoria (en lugar de usar una referencia para manejar un objeto en el montón).
  • las copias pueden / se realizarían "detrás de escena" en algunas situaciones, como pasarlo como argumento a métodos o boxeo (automático o no) creando instancias de envoltorios que contengan otra variable primitiva que tenga el mismo valor.

Mi compañero de trabajo afirma que las primitivas de Java son inmutables y que existe documentación de NSA y Oracle con respecto a la falta de soporte en Java para este requisito.

Mi posición es que las primitivas pueden (al menos en algunas situaciones) ponerse a cero estableciendo el valor en cero (o booleano en falso), y la memoria se borra de esa manera.

Estoy tratando de verificar si hay lenguaje en el JLS u otra documentación "oficial" sobre el comportamiento requerido de las JVM cuando se trata de la administración de la memoria con respecto a las primitivas. Lo más parecido que pude encontrar fue un "Pautas de codificación segura para el lenguaje de programación Java" en el sitio de Oracle que menciona la eliminación de matrices de caracteres después de su uso.

Discutiría sobre las definiciones cuando mi compañero de trabajo llamó a los primitivos inmutables, pero estoy bastante seguro de que quiso decir "la memoria no se puede poner en cero apropiadamente", no nos preocupemos por eso. No discutimos si se refería a las variables finales, del contexto en el que estábamos hablando en general.

¿Hay alguna respuesta definitiva o referencias sobre esto? Agradecería cualquier cosa que pudiera mostrarme dónde estoy equivocado o confirmar que estoy en lo correcto.

Editar : Después de más discusiones, he podido aclarar que mi compañero de trabajo estaba pensando en los envoltorios primitivos, no en los primitivos. Entonces nos queda el problema original de cómo borrar la memoria de forma segura, preferiblemente de objetos. Además, para aclarar, la información confidencial no es solo contraseñas, sino también cosas como direcciones IP o claves de cifrado.

¿Hay JVM comerciales que ofrezcan una característica como el manejo de prioridad de ciertos objetos? (Imagino que esto realmente violaría las especificaciones de Java, pero pensé que podría preguntar en caso de que me equivoque).


Dígales a sus compañeros de trabajo que esta es una causa sin esperanza. ¿Qué pasa con los búferes del socket del núcleo, solo para empezar?

Si no puede evitar que programas no deseados espíen la memoria en su máquina, las contraseñas se verán comprometidas. Período.


Dejando a un lado, pero en algunos entornos, las librerías de seguridad java core usan char [] para que se pueda poner a cero. Me imagino que no obtendrás una garantía aunque.


Editar: De hecho, acabo de tener tres ideas que de hecho pueden funcionar, al menos para diferentes valores de "trabajo".

El primero que está más o menos documentado sería ByteBuffer.allocateDirect! Según tengo entendido, allocateDirect asigna el búfer fuera del montón de Java habitual, por lo que no se copiará. No obstante, no puedo encontrar ninguna garantía dura de que no se copie en todas las situaciones, pero para la máquina virtual Hotspot actual ese es realmente el caso (es decir, se asigna en un montón extra) y supongo que esto seguirá así.

El segundo está usando el paquete sun.misc.unsafe, que como su nombre lo indica tiene algunos problemas bastante obvios, pero al menos eso sería bastante independiente de la VM utilizada, ya sea compatible (y funciona) o no (y obtienes errores de enlace). El problema es que el código para usar ese material se complicará bastante rápido (solo obtener una variable insegura no es trivial).

El tercero sería asignar mucho, mucho, MUCHO más tamaño de lo que realmente se necesita, para que el objeto se asigne en el montón de la vieja generación para comenzar:

l-XX: PretenureSizeThreshold = que se puede establecer para limitar el tamaño de las asignaciones en la generación joven. Cualquier asignación más grande que esto no se intentará en la generación joven y por lo tanto se asignará fuera de la generación anterior.

Bueno, el inconveniente de ESA solución es obvio, creo (el tamaño predeterminado parece ser de alrededor de 64 kb).

. .

De todos modos aquí la vieja respuesta:

Sí, como yo lo veo, no puedes garantizar que los datos almacenados en el montón se eliminen al 100% sin dejar una copia (eso es cierto incluso si no quieres una solución general, sino una que funcione con la máquina virtual Hotspot actual) y sus recolectores de basura por defecto).

Como se dijo en su publicación vinculada ( here ), el recolector de basura hace que esto sea casi imposible de garantizar. En realidad, al contrario de lo que dice la publicación, el problema aquí no es el GC generacional, sino el hecho de que la máquina virtual de Hotspot (y ahora somos específicos de la implementación) está utilizando algún tipo de CG para detener y copiar para su generación joven por defecto.

Esto significa que tan pronto como se produce una recolección de basura entre almacenar la contraseña en la matriz de caracteres y ponerla a cero, obtendrá una copia de los datos que se sobrescribirán tan pronto como se produzca el siguiente GC. Tenga en cuenta que la tenencia de un objeto tendrá exactamente el mismo efecto, pero en lugar de copiarlo en el espacio se copia en el montón de la vieja generación, terminamos con una copia de los datos desde el espacio que no se sobrescribe.

Para evitar este problema, es muy importante que garanticemos que NO haya recolección de basura entre almacenar la contraseña y ponerla a cero O que la matriz de caracteres esté almacenada desde el principio en el montón de la antigua generación. También tenga en cuenta que esto depende de las internas de la máquina virtual Hotspot, que bien puede cambiar (en realidad hay diferentes recolectores de basura donde se pueden generar muchas más copias; iirc la máquina virtual Hotspot admite un GC simultáneo utilizando un algoritmo de tren). "afortunadamente" es imposible garantizar ninguno de estos (¡cada llamada / devolución de un método introduce un punto seguro!), así que ni siquiera te sientes tentado a probarlo (especialmente considerando que no veo ninguna manera de asegurarme de que JIT no optimiza la puesta a cero);)

Parece que la única manera de garantizar que los datos se almacenan solo en una ubicación es usar la JNI para ello.

PD: Tenga en cuenta que aunque lo anterior solo es cierto para el Heap, no se puede garantizar nada más para la pila (el JIT probablemente optimizará las escrituras sin lecturas en la pila, de modo que cuando regrese de la función los datos seguirán siendo en la pila)


He estado tratando de resolver algunos problemas similares con las credenciales.

Hasta ahora, mi única respuesta es "no usar cadenas para secretos". Las cadenas son cómodas de usar y almacenar en términos humanos, pero las computadoras pueden funcionar bien con matrices de bytes. Incluso las primitivas de cifrado funcionan con byte [].

Cuando ya no necesite la contraseña, simplemente complete la matriz con ceros y no permita que el GC invente nuevas formas de reutilizar sus secretos.

En otro hilo ( ¿por qué las cadenas no pueden ser mutables en Java y .NET? ) Suponen que es muy corto de vista. Que las cadenas son inmutables por razones de seguridad; Lo que no se ideó es que no siempre los problemas operativos son los únicos en existencia y que la seguridad a veces necesita cierta flexibilidad y / o soporte para ser efectiva, no existe un soporte en el Java nativo.

Para complementar. ¿Cómo podríamos leer una contraseña sin usar cadenas? Bueno ... sé creativo y no uses cosas como el Android EditText con contraseña de tipo de entrada, eso simplemente no es lo suficientemente seguro y necesitas que vayas a cadenas.


Raro, nunca pensé en algo como esto.

Mi primera idea sería crear un char [100] para almacenar tu contraseña. Pon eso ahí, úsalo para lo que sea, y luego haz un bucle para configurar cada carácter en blanco.

El problema es que la contraseña en algún momento se convertiría en una Cadena dentro del controlador de la base de datos, que podría vivir en la memoria de 0 a infinito segundos.

Mi segunda idea sería realizar toda la autenticación a través de algún tipo de llamada JNI a C, pero sería muy difícil si intentas usar algo como JDBC ....