Protection des champs contre la reflection – Le cas étrange de System.security

Je suis actuellement à la recherche sur la sécurité Java et suis tombé sur un phénomène étrange. Le SecurityManager en java est stocké dans le champ “sécurité” dans java.lang.System. Fait intéressant, le champ semble être protégé contre les access réflexifs, ce qui a du sens, mais pour autant que je sache, ce champ est le seul qui soit. Alors voici l’exemple:

for(Field f : System.class.getDeclaredFields()) System.out.println(f); 

les sorties

 public static final java.io.InputStream java.lang.System.in public static final java.io.PrintStream java.lang.System.out public static final java.io.PrintStream java.lang.System.err private static volatile java.io.Console java.lang.System.cons private static java.util.Properties java.lang.System.props private static java.lang.Ssortingng java.lang.System.lineSeparator 

Fait intéressant: le champ déclaré comme

 private static volatile SecurityManager security = null; 

n’est pas dans la liste, et bien sûr un appel à

 System.class.getDeclaredField("security"); 

donne une exception NoSuchFieldException. Comme je ne trouvais rien sur ce site en ligne et que je suis presque sûr que ce champ était accessible auparavant par reflection (voir aussi, par exemple, ce billet de blog de 2010 décrivant comment accéder à ce champ). Je me demandais a) cette application comme solution rapide pour éviter de désactiver facilement le gestionnaire de sécurité via la reflection et b) comment cela est mis en œuvre (ou plutôt a-t-il une chance de protéger d’autres champs privés de la reflection également).

Un collègue a fait remarquer que la réponse ne se trouvait pas dans le jvm mais dans le jdk, plus précisément dans la classe sun.reflect.Reflection. Vous y trouverez un initialiseur statique qui effectue les opérations suivantes

 static { Map map = new HashMap(); map.put(Reflection.class, new Ssortingng[] {"fieldFilterMap", "methodFilterMap"}); map.put(System.class, new Ssortingng[] {"security"}); fieldFilterMap = map; methodFilterMap = new HashMap(); } 

Si nous regardons maintenant de plus près la méthode getDeclaredFields dans java.lang.Class, nous constaterons que les champs sont filtrés à l’aide d’un appel à la classe Reflection:

 Reflection.filterFields(this, getDeclaredFields0(publicOnly)); 

où filterFields est implémenté en tant que

 public static Field[] filterFields(Class containingClass, Field[] fields) { if (fieldFilterMap == null) { // Bootstrapping return fields; } return (Field[])filter(fields, fieldFilterMap.get(containingClass)); } 

Donc… cela résout le problème de la protection du champ. Je suis toutefois toujours curieux de savoir pourquoi cela a été mis en œuvre.

Premièrement, la façon dont cela a été empêché de réfléchir était probablement un sale problème if dans la JVM, le mécanisme d’obtention de champ:

 if (strcmp(field, "security") == 0 && strcmp(class, "java.lang.System")) { return NULL; 

(Je ne veux PAS dire que c’est le code réel de la machine virtuelle!)

Cela n’est évidemment pas accessible à la plupart des utilisateurs de Java. La seule autre option consiste à installer un gestionnaire de sécurité qui interdit l’access aux champs et méthodes privés. C’est possible, mais je ne sais pas comment.