Comment limiter setAccessible aux seules utilisations «légitimes»?

Plus j’apprenais la puissance de java.lang.reflect.AccessibleObject.setAccessible , plus je suis étonné de ce qu’il peut faire. Ceci est adapté de ma réponse à la question ( Utiliser la reflection pour changer le fichier final statique File.separatorChar pour les tests unitaires ).

 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(Ssortingng args[]) throws Exception { setFinalStatic(Boolean.class.getField("FALSE"), true); System.out.format("Everything is %s", false); // "Everything is true" } } 

Vous pouvez faire des choses vraiment scandaleuses:

 public class UltimateAnswerToEverything { static Integer[] ultimateAnswer() { Integer[] ret = new Integer[256]; java.util.Arrays.fill(ret, 42); return ret; } public static void main(Ssortingng 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" } } 

Vraisemblablement, les concepteurs d’API réalisent à quel point setAccessible peut être setAccessible , mais ils doivent avoir concédé qu’il a des utilisations légitimes pour le fournir. Donc mes questions sont:

  • Quelles sont les utilisations véritablement légitimes de setAccessible ?
    • Java at-il été conçu pour ne pas avoir ce besoin en premier lieu?
    • Quelles seraient les conséquences négatives (le cas échéant) d’une telle conception?
  • Pouvez-vous restreindre setAccessible à des utilisations légitimes?
    • Est-ce uniquement via SecurityManager ?
      • Comment ça marche? Liste blanche / liste noire, granularité, etc.
      • Est-il courant de devoir le configurer dans vos applications?
    • Puis-je écrire mes classes à setAccessible indépendamment de SecurityManager configuration de SecurityManager ?
      • Ou suis-je à la merci de quiconque gère la configuration?

Je suppose qu’une autre question importante est la suivante: Ai-je besoin de travailler à ce sujet ???

Aucune de mes classes ne présente un semblant de vie privée exécutoire. Le modèle singleton (mettre de côté les doutes sur ses mérites) est désormais impossible à appliquer. Comme mes extraits ci-dessus le montrent, même certaines hypothèses de base sur le fonctionnement de Java ne sont pas encore garanties.

Ces problèmes ne sont pas réels ???


Bon, je viens de confirmer: grâce à setAccessible , les chaînes Java ne sont PAS immuables.

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

Suis-je le seul à penser que c’est une préoccupation énorme?

Ai-je besoin de travailler sur ce ???

Cela dépend entièrement des types de programmes que vous écrivez et du type d’architecture.

Si vous dissortingbuez un composant logiciel appelé foo.jar aux gens du monde, vous êtes complètement à leur merci. Ils pourraient modifier les définitions de classe dans votre fichier .jar (par reverse engineering ou par manipulation directe de bytecode). Ils pourraient exécuter votre code dans leur propre JVM, etc. Dans ce cas, vous inquiétez pas.

Si vous écrivez une application Web qui interagit uniquement avec des personnes et des systèmes via HTTP et que vous contrôlez le serveur d’applications, ce n’est pas un problème. Bien sûr, les codeurs de votre entreprise peuvent créer un code qui casse votre modèle de singleton, mais seulement s’ils le souhaitent vraiment.

Si votre future tâche consiste à écrire du code chez Sun Microsystems / Oracle et que vous êtes chargé d’écrire du code pour le kernel Java ou d’autres composants de confiance, vous devez en être conscient. S’inquiéter, cependant, ne fera que vous faire perdre vos cheveux. Dans tous les cas, ils vous feront probablement lire les directives de codage sécurisé avec la documentation interne.

Si vous envisagez d’écrire des applets Java, vous devez connaître la structure de sécurité. Vous constaterez que les applets non signées qui tentent d’appeler setAccessible entraîneront simplement une exception SecurityException.

setAccessible n’est pas la seule chose qui entoure les contrôles d’intégrité conventionnels. Il existe une classe Java principale non-API appelée sun.misc.Unsafe qui peut pratiquement tout faire, y compris accéder directement à la mémoire. Le code natif (JNI) peut également contourner ce type de contrôle.

Dans un environnement en bac à sable (par exemple, Java Applets, JavaFX), chaque classe dispose d’un ensemble d’permissions et d’un access à Unsafe, setAccessible et la définition des implémentations natives est contrôlée par SecurityManager.

“Les modificateurs d’access Java ne sont pas destinés à être un mécanisme de sécurité.”

Cela dépend beaucoup de l’endroit où le code Java est exécuté. Les classes Java principales utilisent des modificateurs d’access comme mécanisme de sécurité pour appliquer le sandbox.

Quelles sont les utilisations véritablement légitimes de setAccessible?

Les classes de base Java l’utilisent comme un moyen simple d’accéder à des éléments devant restr privés pour des raisons de sécurité. À titre d’exemple, la structure de sérialisation Java l’utilise pour appeler des constructeurs d’objects privés lors de la désérialisation d’objects. Quelqu’un a mentionné System.setErr, et ce serait un bon exemple, mais curieusement, les méthodes de la classe System setOut / setErr / setIn utilisent toutes du code natif pour définir la valeur du champ final.

Une autre utilisation légitime évidente est celle des frameworks (persistance, frameworks web, injection) qui ont besoin de se pencher sur l’intérieur des objects.

Les débogueurs, à mon avis, n’entrent pas dans cette catégorie, car ils ne s’exécutent normalement pas dans le même processus JVM, mais à la place de l’interface avec la JVM utilisant d’autres moyens (JPDA).

Java at-il été conçu pour ne pas avoir ce besoin en premier lieu?

C’est une question assez profonde à laquelle il faut bien répondre. J’imagine que oui, mais il faudrait append d’autres mécanismes qui ne seraient peut-être pas si préférables.

Pouvez-vous restreindre setAccessible à des utilisations légitimes?

La ressortingction OOTB la plus simple que vous pouvez appliquer consiste à avoir un SecurityManager et à autoriser setAccessible uniquement au code provenant de certaines sources. C’est ce que fait déjà Java – les classes Java standard provenant de votre JAVA_HOME sont autorisées à faire setAccessible, alors que les classes d’applets non signées de foo.com ne sont pas autorisées à faire setAccessible. Comme cela a été dit précédemment, cette autorisation est binary, dans le sens où l’on l’a ou non. Il n’ya pas de moyen évident d’autoriser setAccessible à modifier certains champs / méthodes en interdisant les autres. En utilisant SecurityManager, vous pouvez toutefois interdire aux classes de référencer complètement certains packages, avec ou sans reflection.

Puis-je écrire mes classes à setAccessible-proof indépendamment de la configuration de SecurityManager? … ou suis-je à la merci de quiconque gère la configuration?

Vous ne pouvez pas et vous le faites certainement.

  • Quelles sont les utilisations véritablement légitimes de setAccessible ?

Tests unitaires, internes de la JVM (par exemple, implémentation de System.setError(...) ) et ainsi de suite.

  • Java at-il été conçu pour ne pas avoir ce besoin en premier lieu?
  • Quelles seraient les conséquences négatives (le cas échéant) d’une telle conception?

Beaucoup de choses ne seraient pas réalisables. Par exemple, la persistance de Java, la sérialisation et les injections de dépendances dépendent de la reflection. Et à peu près tout ce qui repose sur les conventions JavaBeans à l’exécution.

  • Pouvez-vous restreindre setAccessible à des utilisations légitimes?
  • Est-ce uniquement via SecurityManager ?

Oui.

  • Comment ça marche? Liste blanche / liste noire, granularité, etc.

Cela dépend de la permission, mais je crois que la permission d’utiliser setAccessible est binary. Si vous souhaitez une granularité, vous devez soit utiliser un chargeur de classes différent avec un gestionnaire de sécurité différent pour les classes que vous souhaitez restreindre. Je suppose que vous pouvez implémenter un gestionnaire de sécurité personnalisé qui implémente une logique plus fine.

  • Est-il courant de devoir le configurer dans vos applications?

Non.

  • Puis-je écrire mes classes à setAccessible indépendamment de SecurityManager configuration de SecurityManager ?
    • Ou suis-je à la merci de quiconque gère la configuration?

Non, vous ne pouvez pas, et oui vous l’êtes.

L’autre alternative consiste à “appliquer” ceci via des outils d’parsing de code source; Par exemple, les pmd personnalisées pmd ou findbugs . Ou revue de code sélective du code identifié par (disons) grep setAccessible ...

En réponse au suivi

Aucune de mes classes ne présente un semblant de vie privée exécutoire. Le modèle singleton (mettre de côté les doutes sur ses mérites) est désormais impossible à appliquer.

Si cela vous inquiète, alors je suppose que vous devez vous inquiéter. Mais vraiment, vous ne devriez pas essayer de forcer les autres programmeurs à respecter vos décisions de conception. S’ils sont assez stupides pour créer gratuitement plusieurs instances de vos singletons (par exemple), ils peuvent vivre avec les conséquences.

Et si vous voulez dire «protection de la vie privée» pour englober le sens de la protection des informations sensibles contre la divulgation, vous vous trompez également d’arbre. La façon de protéger les données sensibles ne consiste pas à autoriser le code non fiable dans le sandbox de sécurité qui traite des données sensibles. Les modificateurs d’access Java ne sont pas destinés à être un mécanisme de sécurité.

– Suis-je le seul à penser que c’est un problème énorme?

Probablement pas le seul :-). Mais l’OMI, ce n’est pas une préoccupation. Il est admis que le code non fiable doit être exécuté dans un sandbox. Si vous avez fait confiance à du code / à un programmeur fiable pour faire ce genre de choses, alors vos problèmes sont pires que des chaînes mutables de manière inattendue. (Pensez aux bombes logiques …)

La reflection est en effet orthogonale à la sécurité dans cette perspective.

Comment pouvons-nous limiter la reflection?

Java a un gestionnaire de sécurité et ClassLoader comme fondements de son modèle de sécurité. Dans votre cas, je suppose que vous devez regarder java.lang.reflect.ReflectPermission .

Mais cela ne résout pas complètement le problème de la reflection. Les capacités de reflection disponibles doivent être soumises à un régime d’autorisation détaillé, ce qui n’est pas le cas actuellement. Par exemple, pour autoriser certains frameworks à utiliser la reflection (par exemple Hibernate), mais pas le rest de votre code. Ou pour permettre à un programme de refléter uniquement en lecture seule, à des fins de débogage.

Une approche qui pourrait devenir courante à l’avenir est l’utilisation de miroirs pour séparer les capacités de reflection des classes. Voir Miroirs: Principes de conception pour les installations à niveau méta . Il existe cependant diverses autres recherches qui abordent cette question. Mais je suis d’accord que le problème est plus grave pour le langage dynamic que les langages statiques.

Devrions-nous nous inquiéter de la superpuissance que la reflection nous donne? Oui et non.

Oui, dans le sens où la plate-forme Java est supposée être sécurisée avec Classloader et le gestionnaire de sécurité. La capacité de gâcher avec la reflection peut être considérée comme une violation.

Non dans le sens où la plupart des systèmes ne sont de toute façon pas totalement sécurisés. De nombreuses classes peuvent souvent être sous-classées et vous pourriez potentiellement déjà abuser du système avec cela. Bien sûr, les cours peuvent être final ou scellés de manière à ce qu’ils ne puissent pas être sous-classés dans d’autres bocaux. Mais seules quelques classes sont correctement sécurisées (par exemple, Ssortingng).

Voir cette réponse à propos de la classe finale pour une bonne explication. Voir aussi le blog de Sami Koivu pour plus de piratage java autour de la sécurité.

Le modèle de sécurité de Java peut être considéré comme insuffisant à certains égards. Certains langages tels que NewSpeak adoptent une approche encore plus radicale de la modularité, dans laquelle vous n’avez access qu’à ce qui vous est explicitement donné par l’inversion des dépendances (par défaut rien).

Il est également important de noter que la sécurité est de toute façon relative . Au niveau de la langue, vous ne pouvez pas, par exemple, empêcher une forme de module de consumr 100% du processeur ou de consumr toute la mémoire jusqu’à une OutOfMemoryException . Ces préoccupations doivent être abordées par d’autres moyens. Nous verrons peut-être à l’avenir Java étendu avec des quotas d’utilisation des ressources, mais ce n’est pas pour demain 🙂

Je pourrais développer davantage sur le sujet, mais je pense avoir fait valoir mon point.