una - ¿Las clases internas de Java presentan un riesgo de seguridad?
pildorasinformaticas java swing (10)
¿Este comportamiento todavía existe en java 5/6?
No exactamente como se describe; Nunca he visto un compilador donde esto sea cierto:
¡Para permitir que esta clase separada acceda a los campos de la clase externa, el compilador cambia silenciosamente estos campos del ámbito privado al del paquete!
En cambio, IIRC Sun Java 3/4 creó un acceso en lugar de modificar el campo.
Sun Java 6 (javac 1.6.0_16) crea un acceso estático:
public class InnerExample {
private int field = 42;
private class InnerClass {
public int getField () { return field; };
}
private InnerClass getInner () {
return new InnerClass();
}
public static void main (String...args) {
System.out.println(new InnerExample().getInner().getField());
}
}
$ javap -classpath bin -private InnerExample
Compiled from "InnerExample.java"
public class InnerExample extends java.lang.Object{
private int field;
public InnerExample();
private InnerExample$InnerClass getInner();
public static void main(java.lang.String[]);
static int access$000(InnerExample);
}
$ javap -classpath bin -c -private InnerExample
static int access$000(InnerExample);
Code:
0: aload_0
1: getfield #1; //Field field:I
4: ireturn
¿Es esto realmente un riesgo para la seguridad, dado que cualquier clase, aparte de las clases externas e internas, que intentó acceder a los miembros privados de la clase externa no sería [com] ile?
Estoy especulando un poco aquí, pero si compila en contra de la clase no lo hace, pero si agrega el access$000
entonces puede compilar el código que usa el acceso.
import java.lang.reflect.*;
public class InnerThief {
public static void main (String...args) throws Exception {
for (Method me : InnerExample.class.getDeclaredMethods()){
System.out.println(me);
System.out.printf("%08x/n",me.getModifiers());
}
System.out.println(InnerExample.access$000(new InnerExample()));
}
}
Lo interesante es que el acceso sintetizado tiene indicadores de modificador 00001008
donde si agrega un método estático de nivel de paquete, tiene indicadores 00000008
. No hay nada en la segunda edición de la especificación de JVM para ese valor de indicador, pero parece evitar que javac vea el método.
Entonces parece que hay alguna función de seguridad allí, pero no puedo encontrar ninguna documentación sobre ella.
(de ahí esta publicación en CW, en caso de que alguien sepa qué significa 0x1000 en un archivo de clase)
Recientemente, el equipo de seguridad de mi proyecto publicó un documento de pautas de código seguro, diseñado para ser utilizado como parte de las revisiones de nuestro código. Lo primero que me llamó la atención fue un artículo que decía "No use clases internas". Pensé que esto parecía una declaración muy arriesgada y arrolladora. Las clases internas son buenas si se usan correctamente ¿cierto ?, pero hice un poco de búsqueda en Google y encontré esto , citado aquí por conveniencia.
Regla 5: No use clases internas
Algunos libros de lenguaje de Java dicen que las clases externas solo pueden acceder a las clases internas que las encierran. Esto no es verdad. El código de bytes de Java no tiene ningún concepto de clases internas, por lo que las clases internas son traducidas por el compilador en clases ordinarias que resultan accesibles para cualquier código en el mismo paquete. Y la Regla 4 dice que no depende del alcance del paquete para la protección.
Pero espera, se pone peor. Una clase interna obtiene acceso a los campos de la clase externa adjunta, incluso si estos campos se declaran privados. Y la clase interna se traduce a una clase separada. ¡Para permitir que esta clase separada acceda a los campos de la clase externa, el compilador cambia silenciosamente estos campos de ámbito privado a paquete! Ya es suficientemente malo que la clase interna esté expuesta, pero es aún peor que el compilador anule silenciosamente su decisión de hacer que algunos campos sean privados. No use clases internas si puede ayudarlo. (Irónicamente, las nuevas pautas de uso de la API Java 2 doPrivileged () sugieren que use una clase interna para escribir código privilegiado. Esa es una de las razones por las que no nos gusta la API doPrivileged ()).
Mis preguntas son
- ¿Este comportamiento todavía existe en java 5/6?
- ¿Es esto realmente un riesgo de seguridad, dado que cualquier clase, aparte de las clases externas e internas, que intentó acceder a los miembros privados de la clase externa no compilaría?
- ¿Representa un riesgo de seguridad suficiente como para garantizar la ''guía'' ''No usar clases internas''?
¿Este comportamiento todavía existe en java 5/6?
Puede usar la herramienta javap para determinar qué están exponiendo sus binarios y cómo.
package demo;
public class SyntheticAccessors {
private boolean encapsulatedThing;
class Inner {
void doSomething() {
encapsulatedThing = true;
}
}
}
El código anterior (compilado con Sun Java 6 javac
) crea estos métodos en SyntheticAccessors.class
:
Compiled from "SyntheticAccessors.java"
public class demo.SyntheticAccessors extends java.lang.Object{
public demo.SyntheticAccessors();
static void access$0(demo.SyntheticAccessors, boolean);
}
Tenga en cuenta el nuevo método de access$0
.
- Sí, este comportamiento aún existe.
- Es un riesgo de seguridad porque la clase de delincuentes podría diseñarse con algo más que el estándar javac.
- Depende de cuán paranoico sea usted :) Si no permite que las clases alienígenas se ejecuten en su JVM, no veo el problema. Y si lo haces, tienes problemas más grandes (areneros y todo)
- Sé que solo tienes 3 preguntas, pero al igual que otras personas aquí, creo que esta es una restricción estúpida.
"¿Es esto realmente un riesgo de seguridad, dado que ninguna clase, aparte de las clases externas e internas, que intentó acceder a los miembros privados de la clase externa no compilaría?"
Incluso si no se compila en circunstancias normales, aún puede generar su propio bytecode. Pero esa no es razón para evitar las clases internas. Todo lo que tendrías que hacer es asumir que todas tus clases internas son públicas.
Si realmente desea poder ejecutar el código que no es de confianza, aprenda cómo configurar sus propias cajas de arena y niveles de seguridad con The Java Security Architecture , no es tan difícil. Pero principalmente, debe evitar ejecutar código aleatorio en un entorno seguro.
¡disparates! siga la misma lógica, no escriba métodos públicos tampoco, porque tienen acceso a campos privados, ¡brote!
Debería considerar qué tipo de seguridad debe proporcionar su aplicación. Una aplicación con una arquitectura segura no se ejecutará en estos problemas nombrados.
Si hay algo que un usuario no puede hacer con su código, debe separar esta funcionalidad y ejecutarla en un servidor (donde el usuario no tiene acceso a los archivos de la clase).
Recuerde que siempre puede descompilar los archivos de la clase java. Y no confíe en la "seguridad por oscuridad". Incluso el código ofuscado se puede analizar, comprender y modificar.
El código malintencionado puede usar el reflejo de Java para acceder a cualquier información en la JVM a menos que haya un administrador de seguridad que lo prohíba, esto incluye cambiar los campos privados a públicos y mucho más.
Mi opinión personal es que las razones para no hacerlo son abrumadas por las otras posibilidades, por lo que si lo necesita, tiene sentido, y es legible, use clases internas.
La idea de este tipo de seguridad en el código es algo tonto. Si desea seguridad a nivel de código, use una herramienta de ofuscación. Como dijo @skaffman en los comentarios anteriores, "la visibilidad del código nunca ha sido una característica de seguridad. Incluso se puede acceder a los miembros privados mediante la reflexión".
Si está distribuyendo su código compilado y no lo ofusca, usar su clase interna es su última preocupación si le preocupa que las personas jueguen con sus partes privadas.
Si aloja su código, entonces, ¿por qué le preocupa que alguien hurgue en sus clases internas?
Si va a vincular un código de terceros en el que no confía y no puede verificarlo en tiempo de ejecución, agréguelo.
Como dije antes, si esto es realmente una política en su empresa, informe inmediatamente a su compañía a thedailywtf.com
Tenga en cuenta que los inconvenientes enumerados no son válidos para las clases internas static
ya que no tienen acceso implícito a su clase adjunta (u objeto en realidad).
Entonces, si esta regla va a ayudar en su empresa, podría ser una idea para obtener clases internas estáticas excedidas, ya que ofrecen una manera de encapsulación que es útil en muchos casos.
@Tom, citando la especificación del lenguaje Java , "Las clases miembro pueden ser estáticas , en cuyo caso no tienen acceso a las variables de instancia de la clase circundante"
Esta información tiene una década desactualizada. El uso generalizado de clases internas anónimas con AccessController.doPrivileged
debe ser una pista. (Si no te gusta la API, considera la proporción de bloques try
- finally
que faltan incorrectamente en el JDK).
La política es que no hay dos clases que puedan compartir el mismo paquete si son cargadas por diferentes cargadores de clases o tienen certificados diferentes. Para mayor protección, marque los paquetes como sellados en el manifiesto de sus jarras. Entonces, desde el punto de vista de la seguridad, la "Regla 4" es falsa y, por lo tanto, también esta regla.
En cualquier caso, al elaborar las políticas de seguridad, debe comprender aquello contra lo que está protegiendo. Este tipo de políticas son para manejar el código móvil (código que se mueve) que puede tener diferentes niveles de confianza. A menos que esté manejando un código móvil, o su código vaya a una biblioteca a la que pueda ser necesario, hay muy poco sentido en este tipo de precauciones. Sin embargo, casi siempre es una buena idea utilizar un estilo de programación robusto, por ejemplo copiar y validar argumentos y devolver valores.