2010-03-20 13 views
90

Cuanto más aprendí sobre el poder de java.lang.reflect.AccessibleObject.setAccessible, más me asombré de lo que puede hacer. Esto se ha adaptado de mi respuesta a la pregunta (Using reflection to change static final File.separatorChar for unit testing).¿Cómo limitar el acceso inaccesible a usos "legítimos"?

import java.lang.reflect.*; 

public class EverythingIsTrue { 
    static void setFinalStatic(Field field, Object newValue) throws Exception { 
     field.setAccessible(true); 

     Field modifiersField = Field.class.getDeclaredField("modifiers"); 
     modifiersField.setAccessible(true); 
     modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL); 

     field.set(null, newValue); 
    } 
    public static void main(String args[]) throws Exception {  
     setFinalStatic(Boolean.class.getField("FALSE"), true); 

     System.out.format("Everything is %s", false); // "Everything is true" 
    } 
} 

Puede hacer cosas verdaderamente indignante:

public class UltimateAnswerToEverything { 
    static Integer[] ultimateAnswer() { 
     Integer[] ret = new Integer[256]; 
     java.util.Arrays.fill(ret, 42); 
     return ret; 
    } 
    public static void main(String args[]) throws Exception { 
     EverythingIsTrue.setFinalStatic(
     Class.forName("java.lang.Integer$IntegerCache") 
      .getDeclaredField("cache"), 
     ultimateAnswer() 
    ); 
     System.out.format("6 * 9 = %d", 6 * 9); // "6 * 9 = 42" 
    } 
} 

Es de suponer que los diseñadores de la API de darse cuenta de lo que se puede abusar setAccessible puede ser, pero debe de haber admitido que tiene usos legítimos que le proporcione. Entonces mis preguntas son:

  • ¿Cuáles son los usos verdaderamente legítimos para setAccessible?
    • ¿Podría Java haberse diseñado para NO tener esta necesidad en primer lugar?
    • ¿Cuáles serían las consecuencias negativas (si las hay) de dicho diseño?
  • ¿Se puede restringir setAccessible solo para usos legítimos?
    • ¿Es solo a través de SecurityManager?
      • ¿Cómo funciona? ¿Lista blanca/lista negra, granularidad, etc.?
      • ¿Es común tener que configurarlo en sus aplicaciones?
    • puedo escribir mis clases para ser setAccessible -proof independientemente de la configuración SecurityManager?
      • ¿O estoy a merced de quien maneja la configuración?

supongo una pregunta más importante es: ¿Es necesario que preocuparse por esto ???

Ninguna de mis clases tiene ninguna apariencia de privacidad exigible como tal. El patrón singleton (dejando de lado las dudas sobre sus méritos) ahora es imposible de aplicar. Como muestran mis fragmentos anteriores, incluso algunas suposiciones básicas de cómo funciona Java fundamental no están ni siquiera cerca de ser garantizadas.

¿ESTOS PROBLEMAS NO SON REALES?


Vale, acaba de confirmar: gracias a setAccessible, cadenas de Java son NO inmutable.

import java.lang.reflect.*; 

public class MutableStrings { 
    static void mutate(String s) throws Exception { 
     Field value = String.class.getDeclaredField("value"); 
     value.setAccessible(true); 
     value.set(s, s.toUpperCase().toCharArray()); 
    } 
    public static void main(String args[]) throws Exception { 
     final String s = "Hello world!"; 
     System.out.println(s); // "Hello world!" 
     mutate(s); 
     System.out.println(s); // "HELLO WORLD!" 
    } 
} 

¿Soy el único que piensa que esto es una GRAN preocupación?

+24

¡Debería ver lo que pueden hacer en C++! (Ah, y probablemente C#.) –

+5

+1 para la pregunta increíble ... :) – Legend

Respuesta

93

¿NECESITO PREOCUPARME POR ESTO ???

Eso depende completamente de qué tipo de programas está escribiendo y para qué tipo de arquitectura.

Si está distribuyendo un componente de software llamado foo.jar a las personas del mundo, de todos modos está completamente a su merced. Podrían modificar las definiciones de clase dentro de su .jar (mediante ingeniería inversa o manipulación directa de códigos de bytes). Podrían ejecutar su código en su propia JVM, etc. En este caso, preocuparse no servirá de nada.

Si está escribiendo una aplicación web que solo interactúa con personas y sistemas a través de HTTP y controla el servidor de aplicaciones, tampoco es una preocupación. Seguro que los codificadores compañeros en su empresa pueden crear código que rompe su patrón singleton, pero solo si realmente lo desean.

Si su trabajo futuro es escribir código en Sun Microsystems/Oracle y tiene la tarea de escribir código para el núcleo de Java u otros componentes confiables, es algo que debe tener en cuenta. Preocuparse, sin embargo, solo hará que pierdas tu cabello. En cualquier caso, es probable que le hagan leer el Secure Coding Guidelines junto con la documentación interna.

Si va a escribir applets de Java, el marco de seguridad es algo que debe tener en cuenta. Descubrirá que los applets sin firmar que intentan llamar a setAccessible resultarán en una SecurityException.

setAccesible no es lo único que rodea las comprobaciones de integridad convencionales. Hay una clase Java básica no API denominada sun.misc.Unsafe que puede hacer casi cualquier cosa que quiera, incluido el acceso directo a la memoria. El código nativo (JNI) también puede evitar este tipo de control.

En un entorno de espacio aislado (por ejemplo, Java Applets, JavaFX), cada clase tiene un conjunto de permisos y acceso a Inseguro, setAccessible y la definición de implementaciones nativas controladas por SecurityManager.

"Los modificadores de acceso de Java no pretenden ser un mecanismo de seguridad".

Eso depende mucho de dónde se está ejecutando el código de Java.Las clases principales de Java usan modificadores de acceso como un mecanismo de seguridad para aplicar el entorno limitado.

¿Cuáles son los verdaderos usos legítimos de setAccessible?

Las clases principales de Java lo utilizan como una manera fácil de acceder a cosas que deben permanecer privadas por razones de seguridad. Como ejemplo, el marco de Serialización de Java lo usa para invocar constructores de objetos privados al deserializar objetos. Alguien mencionó System.setErr, y sería un buen ejemplo, pero curiosamente los métodos de clase del sistema setOut/setErr/setIn usan código nativo para establecer el valor del campo final.

Otro uso legítimo obvio son los marcos (persistencia, marcos web, inyección) que necesitan asomarse al interior de los objetos.

Los depuradores, en mi opinión, no entran en esta categoría, ya que normalmente no se ejecutan en el mismo proceso de JVM, sino que en su lugar utilizan la interfaz con la JVM por otros medios (JPDA).

¿Podría Java ha sido diseñado para NO tener esta necesidad en primer lugar?

Esa es una pregunta bastante profunda para responder bien. Me imagino que sí, pero necesitarías agregar algún otro mecanismo que no sea tan preferible.

¿Se puede restringir setAccessible solo para usos legítimos?

La restricción OOTB más directa que puede aplicar es tener un SecurityManager y permitir que setAccessible solo provenga de ciertas fuentes. Esto es lo que Java ya hace: las clases estándar de Java que provienen de su JAVA_HOME pueden hacer setAccessible, mientras que las clases de applet sin firma de foo.com no tienen permitido hacer setAccessible. Como se dijo anteriormente, este permiso es binario, en el sentido de que uno lo tiene o no. No hay una manera obvia de permitir que setAccessible modifique ciertos campos/métodos mientras no se permiten otros. Sin embargo, al usar SecurityManager, puede prohibir que las clases hagan referencia a ciertos paquetes por completo, con o sin reflexión.

¿Puedo escribir mis clases para establecerlas a prueba de acceso independientemente de la configuración de SecurityManager? ... ¿O estoy a merced de quien maneja la configuración?

No puede y seguramente lo es.

+7

+1 y bienvenidos a Stack Overflow :) – ewernli

+0

Gracias, amable señor (para el enlace entrante a mi blog, también) :) –

+0

+1 ¡Respuesta de Greaaat, gracias! – reef

14
  • Cuáles son los usos verdaderamente legítimas para setAccessible?

Prueba de la unidad, internos de la JVM (por ejemplo, la aplicación de System.setError(...)) y así sucesivamente.

  • Podría Java ha sido diseñado de manera que no tiene esta necesidad, en primer lugar?
  • ¿Cuáles serían las consecuencias negativas (si las hay) de dicho diseño?

Muchas cosas no se pueden aplicar. Por ejemplo, varias inyecciones de persistencia, serialización y dependencia de Java dependen de la reflexión. Y prácticamente todo lo que dependa de las convenciones de JavaBeans en tiempo de ejecución.

  • ¿Se puede restringir a setAccessible únicos usos legítimos?
  • ¿Es solo a través de SecurityManager?

Sí.

  • ¿Cómo funciona? ¿Lista blanca/lista negra, granularidad, etc.?

Depende del permiso, pero yo creo que el permiso para utilizar setAccessible es binario. Si desea granularidad, necesita usar un cargador de clases diferente con un administrador de seguridad diferente para las clases que desee restringir. Supongo que podría implementar un administrador de seguridad personalizado que implemente una lógica granulada más fina.

  • ¿Es común tener que configurarlo en sus aplicaciones?

  • Puedo escribir mis clases para ser setAccessible -proof independientemente de la configuración SecurityManager?
    • ¿O estoy a merced de quien maneja la configuración?

No, no puedes, y sí que eres.

La otra alternativa es "hacer cumplir" esto a través de herramientas de análisis de código fuente; p.ej. reglas personalizadas pmd o findbugs. O la revisión selectiva del código identificado por (digamos) grep setAccessible ....

En respuesta al seguimiento

Ninguno de mis clases tienen una apariencia de privacidad aplicable lo que tan nunca. El patrón singleton (dejando de lado las dudas sobre sus méritos) ahora es imposible de aplicar.

Si eso te preocupa, entonces supongo que debes preocuparte. Pero realmente no deberías intentarlo puede forzar otros programadores para respetar sus decisiones de diseño. Si son lo suficientemente estúpidos como para crear gratuitamente instancias múltiples de sus singletons (por ejemplo), pueden vivir con las consecuencias.

Y si quiere decir "privacidad" para abarcar el significado de proteger la información sensible de la divulgación, también está ladrando al árbol equivocado. La forma de proteger los datos confidenciales no es permitir que el código que no es de confianza entre en el entorno limitado de seguridad que trata con datos confidenciales. Los modificadores de acceso de Java no están destinados a ser un mecanismo de seguridad.

< ejemplo de cadena > - ¿Soy el único que piensa que esto es una preocupación enorme?

Probablemente no es la única uno :-). Pero OMI, esto no es una preocupación. Es un hecho aceptado que el código que no es de confianza se debe ejecutar en una caja de arena. Si tiene un código de confianza/un programador de confianza que hace cosas como esta, entonces sus problemas son peores que tener cadenas inesperadamente mutables. (Creo que las bombas lógicas ...)

+1

También es útil para cosas como implementaciones de persistencia. Reflections no es realmente útil para los depuradores (aunque originalmente estaba destinado a ser). No puede cambiar (subclasificar) de forma útil el 'SecurityManager' para esto, porque todo lo que obtiene es la comprobación de permisos; todo lo que realmente puede hacer es mirar el acc y quizás caminar por la pila comprobar el hilo actual). 'grep java.lang.reflect' –

+0

@Stephen C: +1 ya, pero también agradecería su opinión sobre una pregunta más que acabo de agregar. Gracias por adelantado. – polygenelubricants

+0

Tenga en cuenta que grep es una idea terrible. Hay tantas formas de ejecutar código dinámicamente en Java que casi seguramente perderá uno. El código de la lista negra en general es una receta para el fracaso. – Antimony

11

La reflexión es ciertamente ortogonal a la seguridad bajo esta perspectiva.

¿Cómo podemos limitar la reflexión?

Java tiene el administrador de seguridad y ClassLoader como fundamentos de su modelo de seguridad. En tu caso, supongo que debes mirar java.lang.reflect.ReflectPermission.

Pero esto no resuelve por completo el problema de la reflexión. Las capacidades de reflexión que están disponibles deberían estar sujetas a un esquema de autorización detallado que no es el caso ahora. P.ej. para permitir que cierto marco utilice la reflexión (por ejemplo, Hibernar), pero no el resto de su código. O para permitir que un programa se refleje solo de forma de solo lectura, con fines de depuración.

Un enfoque que puede convertirse en la corriente principal en el futuro es el uso de los llamados espejos para separar las capacidades reflectantes de las clases. Ver Mirrors: Design Principles for Meta-level Facilities. Sin embargo, hay varios other research que abordan este problema. Pero estoy de acuerdo en que el problema es más severo para el lenguaje dinámico que los lenguajes estáticos.

¿Deberíamos estar preocupados por la superpotencia que nos brinda la reflexión? Sí y no.

en el sentido de que se supone que la plataforma Java está asegurada con Classloader y administrador de seguridad. La capacidad de meterse con la reflexión se puede ver como una brecha.

No en el sentido de que la mayoría de los sistemas no son del todo seguros.Muchas clases se pueden subclasificar con frecuencia y es posible que ya abuses del sistema con eso. Por supuesto, las clases se pueden hacer final o sealed para que no se puedan subclasificar en otro jar. Pero solo unas pocas clases están aseguradas correctamente (por ejemplo, String) de acuerdo con esto.

Vea esto answer about final class para una buena explicación. Ver también el blog de Sami Koivu para más pirateo de java en torno a la seguridad.

El modelo de seguridad de Java puede considerarse insuficiente para algunos aspectos. Algunos lenguajes, como NewSpeak, adoptan un enfoque aún más radical respecto de la modularidad, donde solo se tiene acceso a lo que explícitamente se le otorga mediante la inversión de la dependencia (por defecto nada).

También es importante tener en cuenta que la seguridad es de todos modos relativa. En el nivel de idioma, por ejemplo, no puede evitar que un módulo consuma el 100% de la CPU o consuma toda la memoria hasta OutOfMemoryException. Tales preocupaciones deben abordarse por otros medios. Tal vez veamos en el futuro que Java se extienda con cuotas de utilización de recursos, pero no es para mañana :)

Podría ampliar más sobre el tema, pero creo que ya lo mencioné.

Cuestiones relacionadas