100 votes

Comment limiter setAccessible aux seules utilisations "légitimes" ?

Plus j'en apprenais sur le pouvoir 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 ( Utilisation de la réflexion pour modifier le fichier statique final 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(String 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(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"
   }
}

On peut supposer que les concepteurs de l'API se rendent compte de l'utilisation abusive setAccessible peut être, mais doit avoir concédé qu'il a des utilisations légitimes pour le fournir. Donc mes questions sont :

  • Quelles sont les utilisations vraiment légitimes de setAccessible ?
    • Java aurait-il pu être conçu de manière à 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 fins légitimes uniquement ?
    • Est-ce que c'est seulement par SecurityManager ?
      • Comment cela fonctionne-t-il ? Liste blanche/liste noire, granularité, etc ?
      • Est-il courant de devoir le configurer dans vos applications ?
    • Puis-je écrire mes classes pour qu'elles soient setAccessible -sans tenir compte de SecurityManager configuration ?
      • Ou suis-je à la merci de celui qui gère la configuration ?

Je suppose qu'une question plus importante est : DOIS-JE M'INQUIÉTER DE CELA ?

Aucun de mes cours n'a un semblant de confidentialité applicable, quel qu'il soit. Le modèle singleton (en mettant de côté les doutes sur ses mérites) est maintenant impossible à appliquer. Comme le montrent mes extraits ci-dessus, même certaines hypothèses de base sur le fonctionnement fondamental de Java sont loin d'être garanties.

CES PROBLÈMES NE SONT-ILS PAS RÉELS ?


Ok, je viens de confirmer : grâce à setAccessible les chaînes de caractères Java sont PAS immuable.

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!"
   }
}

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

30 votes

Vous devriez voir ce qu'ils peuvent faire en C++ ! (Oh, et probablement en C#.)

106voto

Sami Koivu Points 2420

EST-CE QUE JE DOIS M'INQUIÉTER DE ÇA ???

Cela dépend entièrement des types de programmes que vous écrivez et pour quel type d'architecture.

Si vous distribuez un composant logiciel appelé foo.jar aux gens du monde entier, vous êtes de toute façon complètement à leur merci. Ils peuvent modifier les définitions des classes à l'intérieur de votre .jar (par rétro-ingénierie ou manipulation directe du bytecode). Ils pourraient exécuter votre code dans leur propre JVM, etc. Dans ce cas, l'inquiétude ne vous servira à rien.

Si vous écrivez une application web qui ne s'interface avec des personnes et des systèmes que via HTTP et que vous contrôlez le serveur d'application, ce n'est pas non plus un problème. Bien sûr, les autres codeurs de votre entreprise peuvent créer du code qui brise votre modèle singleton, mais seulement s'ils le veulent vraiment.

Si votre futur emploi consiste à écrire du code chez Sun Microsystems/Oracle et que vous êtes chargé d'écrire du code pour le noyau Java ou d'autres composants de confiance, vous devez en être conscient. Cependant, vous inquiéter ne fera que vous faire perdre vos cheveux. Dans tous les cas, ils vous feront probablement lire les Directives de codage sécurisé ainsi que la documentation interne.

Si vous avez l'intention d'écrire des applets Java, vous devez connaître le cadre de sécurité. Vous constaterez que les applets non signées qui tentent d'appeler setAccessible se solderont par une SecurityException.

setAccessible n'est pas la seule chose qui contourne les contrôles d'intégrité conventionnels. Il existe une classe Java de base non-API appelée sun.misc.Unsafe qui peut faire à peu près tout ce qu'elle veut, y compris accéder directement à la mémoire. Le code natif (JNI) peut également contourner ce type de contrôle.

Dans un environnement sandbox (par exemple les applets Java, JavaFX), chaque classe possède un ensemble de permissions et l'accès à Unsafe, setAccessible et la définition des implémentations natives sont contrôlés par le SecurityManager.

"Les modificateurs d'accès Java ne sont pas destinés à être un mécanisme de sécurité".

Cela dépend en grande partie de l'endroit où le code Java est exécuté. Les classes de base de Java utilisent des modificateurs d'accès comme mécanisme de sécurité pour faire respecter le bac à sable.

Quelles sont les utilisations vraiment légitimes de setAccessible ?

Les classes principales de Java l'utilisent comme un moyen facile d'accéder à des éléments qui doivent rester privés pour des raisons de sécurité. Par exemple, le cadre de sérialisation de Java l'utilise pour invoquer les constructeurs d'objets privés lors de la désérialisation des objets. 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 cadres (persistance, cadres web, injection) qui ont besoin de jeter un coup d'œil à l'intérieur des objets.

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 s'interfacent avec la JVM par d'autres moyens (JPDA).

Java aurait-il pu être conçu de manière à 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 ajouter un ou plusieurs autres mécanismes qui ne seraient peut-être pas si préférables que ça.

Pouvez-vous restreindre setAccessible aux seules utilisations légitimes ?

La restriction OOTB la plus directe que vous pouvez appliquer est d'avoir un SecurityManager et de n'autoriser setAccessible qu'au code provenant de certaines sources. C'est ce que fait déjà Java - les classes Java standard qui proviennent de votre JAVA_HOME sont autorisées à faire setAccessible, tandis que les classes d'applets non signées de foo.com ne sont pas autorisées à faire setAccessible. Comme il a été dit précédemment, cette permission est binaire, dans le sens où l'on en dispose ou non. Il n'y a pas de moyen évident de permettre à setAccessible de modifier certains champs/méthodes tout en interdisant d'autres. En utilisant le SecurityManager, vous pouvez cependant interdire aux classes de faire référence à certains paquets, avec ou sans réflexion.

Puis-je écrire mes classes pour qu'elles soient setAccessible-proof indépendamment de la configuration du SecurityManager ? ... Ou suis-je à la merci de celui qui gère la configuration ?

Vous ne pouvez pas et vous l'êtes très certainement.

0 votes

"Si vous distribuez un composant logiciel appelé foo.jar aux gens du monde entier, vous êtes de toute façon complètement à leur merci. Ils pourraient modifier les définitions des classes à l'intérieur de votre .jar (par rétro-ingénierie ou manipulation directe du bytecode). Ils pourraient exécuter votre code dans leur propre JVM, etc. Dans ce cas, l'inquiétude ne vous servira à rien." Est-ce toujours le cas ? Des conseils pour rendre la manipulation des logiciels plus difficile (de manière significative, espérons-le) ? Il est difficile de jeter les applications de bureau java par la fenêtre à cause de cela.

0 votes

@naaz Je dois dire que rien n'a changé. C'est juste que l'architecture travaille contre vous. Tout logiciel fonctionnant sur ma machine et dont j'ai le contrôle total peut être modifié. La dissuasion la plus forte pourrait être d'ordre juridique, mais je n'imagine pas que cela fonctionne pour tous les scénarios. Je pense que les binaires Java sont parmi les plus faciles à inverser, mais les personnes expérimentées sont capables de modifier n'importe quoi. L'obscurcissement aide en rendant difficile les changements importants, mais toute sorte de booléen (comme un contrôle de copyright) est toujours trivial à contourner. Vous pourriez essayer de mettre les parties clés sur un serveur à la place.

0 votes

Que diriez-vous de denuvo ?

15voto

Stephen C Points 255558
  • Quelles sont les utilisations vraiment légitimes de setAccessible ?

Les tests unitaires, les éléments internes de la JVM (par exemple, l'implémentation de la System.setError(...) ) et ainsi de suite.

  • Java aurait-il pu être conçu de manière à 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 seraient impossibles à mettre en œuvre. Par exemple, diverses persistance Java, sérialisation et injections de dépendances dépendent de la réflexion. Et à peu près tout ce qui repose sur les conventions JavaBeans au moment de l'exécution.

  • Pouvez-vous restreindre setAccessible à des fins légitimes uniquement ?
  • Est-ce que c'est seulement par SecurityManager ?

Sí.

  • Comment cela fonctionne-t-il ? Liste blanche/liste noire, granularité, etc ?

Cela dépend de la permission, mais je crois que la permission d'utiliser setAccessible est binaire. Si vous voulez de la granularité, vous devez soit utiliser un chargeur de classes différent avec un gestionnaire de sécurité différent pour les classes que vous voulez restreindre. Je suppose que vous pourriez mettre en œuvre un gestionnaire de sécurité personnalisé qui implémente une logique à grain plus fin.

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

Non.

  • Puis-je écrire mes classes pour qu'elles soient setAccessible -sans tenir compte de SecurityManager configuration ?
    • Ou suis-je à la merci de celui qui gère la configuration ?

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

L'autre solution consiste à "imposer" cette règle par l'intermédiaire d'outils d'analyse du code source, par exemple des outils personnalisés. pmd o findbugs règles. Ou une revue de code sélective du code identifié par (disons) grep setAccessible ... .

En réponse au suivi

Aucun de mes cours n'a un semblant de confidentialité applicable, quel qu'il soit. Le modèle singleton (en mettant de côté les doutes sur ses mérites) est maintenant impossible à appliquer.

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

En revanche, si vous entendez par "vie privée" la protection des informations sensibles contre la divulgation, vous faites fausse route. La façon de protéger les données sensibles dans une application Java est de ne pas permettre au code non fiable d'entrer dans le bac à sable de sécurité qui traite les données sensibles. Les modificateurs d'accès Java ne sont pas destinés à être un mécanisme de sécurité.

<String example> - Suis-je le seul à penser qu'il s'agit d'un ÉNORME problème ?

Probablement pas le seulement un :-). Mais, selon l'OMI, ce n'est pas un problème. Il est admis que le code non fiable doit être exécuté dans un bac à sable. Si vous avez du code de confiance / un programmeur de confiance qui fait des choses comme ça, alors vos problèmes sont pires que des chaînes mutables de manière inattendue. (Pensez à des bombes logiques, à l'exfiltration de données par le biais de canaux secrets etcetera)

Il existe des moyens de traiter (ou d'atténuer) le problème d'un "mauvais acteur" dans votre équipe de développement ou d'exploitation. Mais elles sont coûteuses et restrictives... et excessives pour la plupart des cas d'utilisation.

1 votes

Également utile pour des choses telles que les implémentations de persistance. Reflections n'est pas vraiment utile pour les débogueurs (bien qu'il ait été conçu à l'origine pour l'être). Vous ne pouvez pas modifier utilement (sous-classer) la classe SecurityManager pour cela, parce que tout ce que vous obtenez est la vérification de la permission - tout ce que vous pouvez vraiment faire est de regarder l'acc et peut-être de parcourir la pile de vérification du thread actuel). grep java.lang.reflect.

0 votes

@Stephen C : +1 déjà, mais j'aimerais aussi avoir votre avis sur une autre question que je viens d'ajouter. Merci d'avance.

0 votes

Notez que grep est une idée terrible. Il existe tellement de façons d'exécuter dynamiquement du code en Java que vous en manquerez presque certainement une. La mise sur liste noire de code en général est une recette pour l'échec.

11voto

ewernli Points 23180

La réflexion est en effet orthogonale à la sûreté/sécurité dans cette perspective.

Comment pouvons-nous limiter la réflexion ?

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

Mais cela ne résout pas complètement le problème de la réflexion. Les capacités de réflexion disponibles devraient être soumises à un schéma d'autorisation à grain fin, ce qui n'est pas le cas actuellement. Par exemple, autoriser certains frameworks à utiliser la réflexion (par exemple Hibernate), mais pas le reste de votre code. Ou pour permettre à un programme de réfléchir uniquement en lecture seule, à des fins de débogage.

Une approche qui pourrait se généraliser à l'avenir est l'utilisation de ce que l'on appelle le miroirs pour séparer les capacités de réflexion des classes. Voir Miroirs : Principes de conception pour les installations de niveau méta . Il existe cependant plusieurs autre recherche qui aborde cette question. Mais je reconnais que le problème est plus grave pour les langages dynamiques que pour les langages statiques.

Devons-nous nous inquiéter du super pouvoir que nous donne la réflexion ? Oui et non.

Oui dans le sens où la plate-forme Java est censée être sécurisée avec Classloader et responsable de la sécurité. La possibilité de jouer avec la réflexion 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 entièrement sécurisés. De nombreuses classes peuvent fréquemment être sous-classées et vous pouvez potentiellement abuser du système avec cela. Bien sûr, les classes peuvent être faites final ou scellé afin qu'ils ne puissent pas être sous-classés dans d'autres jar. Mais seulement quelques classes sont sécurisées correctement (par exemple String) selon ceci.

Voir ceci réponse sur la classe finale pour une bonne explication. Voir aussi le blog de Sami Koivu pour plus de java hacking 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 adopter une approche encore plus radicale de la modularité, où vous n'avez accès qu'à ce qui vous est explicitement donné par l'inversion de dépendance (rien par défaut).

Il est également important de noter que la sécurité est de toute façon relatif . Au niveau du langage, vous pouvez par exemple ne pas empêcher un module de consommer 100 % de l'unité centrale ou de consommer toute la mémoire jusqu'à un certain seuil. OutOfMemoryException . Ces problèmes doivent être résolus 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 m'étendre davantage sur le sujet, mais je pense avoir fait le tour de la question.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X