Plus j'en ai appris sur le pouvoir 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 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(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"
}
}
Vraisemblablement, les concepteurs d'API réalisent à quel point il setAccessible
peut être abusif , mais doivent avoir admis qu'il a des utilisations légitimes pour le fournir. Donc mes questions sont:
- Quelles sont les utilisations vraiment légitimes
setAccessible
?- Java a-t-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 limiter
setAccessible
uniquement les utilisations légitimes?- Est-ce seulement à travers
SecurityManager
?- Comment ça marche? Liste blanche / liste noire, granularité, etc.?
- Est-il courant de devoir le configurer dans vos applications?
- Puis-je écrire mes cours pour être à l'
setAccessible
épreuve quelle que soit laSecurityManager
configuration?- Ou suis-je à la merci de celui qui gère la configuration?
- Est-ce seulement à travers
Je suppose qu'une autre question importante est: DOIS-JE M'INQUIÉTER DE CELA ???
Aucune de mes classes n'a le moindre semblant de confidentialité exécutoire. Le modèle singleton (mettant de côté les doutes sur ses mérites) est désormais impossible à appliquer. Comme le montrent mes extraits ci-dessus, même certaines hypothèses de base sur le fonctionnement de Java fondamental ne sont même pas proches d'être garanties.
CES PROBLÈMES NE SONT-ILS PAS RÉELS ???
D'accord, je viens de confirmer: grâce à setAccessible
, les chaînes Java ne sont PAS immuables.
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?
la source
Réponses:
Dois-je m'inquiéter à ce sujet ???
Cela dépend entièrement des types de programmes que vous écrivez et du 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 pourraient modifier les définitions de classe à l'intérieur de votre .jar (par ingénierie inverse ou manipulation directe de bytecode). Ils pourraient exécuter votre code dans leur propre JVM, etc. Dans ce cas, vous inquiéter ne vous servira à rien.
Si vous écrivez une application Web qui ne s'interface qu'avec des personnes et des systèmes via HTTP et que vous contrôlez le serveur d'applications, ce n'est pas non plus un problème. Bien sûr, les codeurs de votre entreprise peuvent créer du code qui rompt votre modèle singleton, mais seulement s'ils le souhaitent vraiment.
Si votre futur travail 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. 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é ainsi que la documentation interne.
Si vous allez écrire des applets Java, vous devez être conscient du cadre de sécurité. Vous constaterez que les applets non signées qui tentent d'appeler setAccessible entraîneront simplement 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, applets Java, JavaFX), chaque classe dispose d'un ensemble d'autorisations et l'accès aux implémentations non sécurisées, setAccessible et natives est contrôlé par SecurityManager.
"Les modificateurs d'accès 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 de base utilisent des modificateurs d'accès comme mécanisme de sécurité pour appliquer le bac à sable.
Quelles sont les utilisations vraiment légitimes de setAccessible?
Les classes Java Core 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, l'infrastructure de sérialisation Java l'utilise pour appeler des constructeurs d'objets privés lors de la désérialisation d'objets. Quelqu'un a mentionné System.setErr, et ce serait un bon exemple, mais curieusement, les méthodes de classe System setOut / setErr / setIn utilisent toutes du code natif pour définir la valeur du champ final.
Une autre utilisation légitime évidente sont les frameworks (persistance, frameworks web, injection) qui ont besoin de jeter un œil à l'intérieur des objets.
Les débogueurs, à mon avis, ne rentrent pas dans cette catégorie, car ils ne fonctionnent normalement pas dans le même processus JVM, mais plutôt dans l'interface avec la JVM en utilisant d'autres moyens (JPDA).
Java a-t-il été conçu pour ne PAS avoir ce besoin en premier lieu?
C'est une question assez profonde à bien répondre. J'imagine que oui, mais vous devrez ajouter d'autres mécanismes qui pourraient ne pas être tout à fait préférables.
Pouvez-vous limiter setAccessible à des utilisations légitimes uniquement?
La restriction OOTB la plus simple que vous puissiez appliquer est d'avoir un SecurityManager et d'autoriser setAccessible uniquement 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'applet non signées de foo.com ne sont pas autorisées à faire setAccessible. Comme cela a été dit précédemment, cette permission est binaire, en ce sens que l'on l'a ou non. Il n'y a aucun moyen évident d'autoriser setAccessible à modifier certains champs / méthodes tout en interdisant d'autres. En utilisant SecurityManager, vous pouvez cependant interdire aux classes de référencer complètement certains packages, avec ou sans réflexion.
Puis-je écrire mes classes pour qu'elles soient définies comme étant à l'épreuve des accès quelle que soit la configuration de SecurityManager? ... Ou suis-je à la merci de celui qui gère la configuration?
Vous ne pouvez pas et vous l'êtes certainement.
la source
Tests unitaires, internes de la JVM (par exemple, implémentation
System.setError(...)
), etc.Beaucoup de choses seraient inimplémentables. Par exemple, diverses injections de persistance, de sérialisation et de dépendances Java dépendent de la réflexion. Et à peu près tout ce qui repose sur les conventions JavaBeans au moment de l'exécution.
Oui.
Cela dépend de l'autorisation, mais je pense que l'autorisation d'utilisation
setAccessible
est binaire. Si vous voulez de la granularité, vous devez soit utiliser un chargeur de classe 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.Non.
Non, vous ne pouvez pas, et oui vous l'êtes.
L'autre alternative est de «faire respecter» cela via des outils d'analyse de code source; par exemple, coutume
pmd
oufindbugs
règles. Ou examen sélectif du code du code identifié par (par exemple)grep setAccessible ...
.En réponse au suivi
Si cela vous inquiète, alors je suppose que vous devez vous inquiéter. Mais en réalité, vous ne devriez pas essayer de forcer d' autres programmeurs à respecter vos décisions de conception. Si les gens sont assez stupides pour utiliser la réflexion pour créer gratuitement plusieurs instances de vos singletons (par exemple), ils peuvent vivre avec les conséquences.
D'un autre côté, si vous voulez dire «vie privée» pour englober le sens de la protection des informations sensibles contre la divulgation, vous aboyez le mauvais arbre. La manière de protéger les données sensibles dans une application Java consiste à ne pas autoriser le code non approuvé dans le sandbox 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é.
Probablement pas le seul :-). Mais l'OMI, ce n'est pas un problème. Il est admis que le code non approuvé doit être exécuté dans un bac à sable. Si vous avez du code de confiance / un programmeur de confiance faisant des choses comme celle-ci, vos problèmes sont pires que des chaînes mutables de manière inattendue. (Pensez aux bombes logiques, à l'exfiltration de données via des canaux secrets , etc.)
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 ils sont coûteux et restrictifs ... et exagérés pour la plupart des cas d'utilisation.
la source
SecurityManager
pour cela, car tout ce que vous obtenez est la vérification des autorisations - tout ce que vous pouvez vraiment faire est de regarder l'acc et peut-être de parcourir la pile pour vérifier le thread actuel).grep java.lang.reflect.
La réflexion est en effet orthogonale à la sûreté / sécurité dans cette perspective.
Comment limiter la réflexion?
Java a un gestionnaire de sécurité et
ClassLoader
comme base de son modèle de sécurité. Dans votre cas, je suppose que vous devez regarderjava.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 système d'autorisation précis, ce qui n'est pas le cas actuellement. Par exemple, pour autoriser certains framework à utiliser la réflexion (par exemple Hibernate), mais pas le reste 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 ce que l'on appelle des miroirs pour séparer les capacités de réflexion des classes. Voir Miroirs: principes de conception pour les installations de méta-niveau . Il existe cependant diverses autres recherches qui abordent ce problème. Mais je conviens que le problème est plus grave pour le langage dynamique que pour les langages statiques.
Doit-on s'inquiéter de la superpuissance 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
un gestionnaire de sécurité. La capacité de jouer avec la réflexion peut être considérée comme une brèche.Non, dans le sens où la plupart des systèmes ne sont de toute façon pas entièrement sécurisés. Un grand nombre de classes peuvent souvent être sous-classées et vous pourriez déjà abuser du système avec juste cela. Bien sûr, les classes peuvent être faites
final
, ou scellées afin qu'elles ne puissent pas être sous-classées dans d'autres bocaux. Mais seules quelques classes sont correctement sécurisées (par exemple String) en fonction de cela.Voir cette réponse sur la classe finale pour une explication intéressante. Voir également 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 comme NewSpeak adoptent 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épendances (par défaut rien).
Il est également important de noter que la sécurité est de toute façon relative . Au niveau du langage, vous ne pouvez par exemple pas empêcher une forme de module de consommer 100% du CPU ou de consommer toute la mémoire jusqu'à un
OutOfMemoryException
. Ces préoccupations doivent être traitées par d’autres moyens. Nous verrons peut-être dans le futur 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.
la source