31 votes

Les classes internes de Java présentent-elles un risque pour la sécurité ?

Récemment, l'équipe de sécurité de mon projet a publié un document sur les lignes directrices en matière de code sécurisé, conçu pour être utilisé dans le cadre de nos examens de code. La première chose qui m'a frappé est un point qui dit "Ne pas utiliser de classes internes". Cela m'a semblé être une déclaration très lourde et très générale. Les classes internes sont bonnes si elles sont utilisées correctement, n'est-ce pas ? mais j'ai fait un peu de recherche sur Google et j'ai trouvé cette cité ici par souci de commodité.

Règle 5 : Ne pas utiliser de classes internes

Certains ouvrages sur le langage Java indiquent que classes internes ne peuvent être accédées que par les par les classes externes qui les entourent. Ce n'est pas le cas. Le code d'octets Java a n'a pas de concept de classes internes, de sorte que les sont traduites par le compilateur en classes ordinaires qui se trouvent être qui se trouvent être accessibles à tout code dans le même même paquet. Et la règle 4 dit de ne pas dépendre de l'étendue du paquetage pour la protection.

Mais attendez, il y a pire. Une classe a accès aux champs de la classe extérieure de la classe externe qui l'entoure, même si ces sont déclarés privés. Et la est traduite en une classe classe distincte. Afin de permettre à cette classe distincte d'accéder aux champs de la la classe externe, le compilateur modifie silencieusement silencieusement ces champs de privé à en champ d'application du paquetage ! [ ] que la classe interne soit exposée, mais c'est encore pire que le compilateur silencieusement votre décision de de rendre certains champs privés. N'utilisez pas de classes internes si vous pouvez l'éviter. (Ironiquement, la nouvelle utilisation de l'API Java 2 doPrivileged() de l'API Java 2 suggèrent d'utiliser une classe interne pour pour écrire du code privilégié. C'est l'une des raison pour laquelle nous n'aimons pas l'API doPrivileged()).

Mes questions sont les suivantes

  1. Ce comportement existe-t-il encore dans java 5 / 6 ?
  2. S'agit-il réellement d'un risque de sécurité, étant donné que toute classe, autre que les classes externe et interne, qui tenterait d'accéder aux membres privés de la classe externe ne se compilerait pas ?
  3. Cela pose-t-il un risque de sécurité suffisant pour justifier la "ligne directrice" "Ne pas utiliser de classes internes" ?

18voto

Tom Hawtin - tackline Points 82671

Ces informations sont dépassées d'une dizaine d'années. L'utilisation généralisée de classes internes anonymes avec des AccessController.doPrivileged devrait être un indice. (Si vous n'aimez pas l'API, considérez la proportion de try - finally qui manquent par erreur dans le JDK).

La politique veut que deux classes ne puissent pas partager le même paquetage si elles sont chargées par des chargeurs de classe différents ou si elles ont des certificats différents. Pour plus de protection, marquez les paquets comme scellés dans le manifeste de vos pots. Donc, du point de vue de la sécurité, la "règle 4" est fausse et donc aussi cette règle.

En tout état de cause, lorsque vous élaborez des politiques de sécurité, vous devez savoir contre quoi vous vous protégez. Ce type de politique concerne le traitement du code mobile (code qui se déplace) qui peut avoir différents niveaux de confiance. À moins que vous ne manipuliez du code mobile ou que votre code soit intégré dans une bibliothèque qui peut être tenue de le faire, il n'y a que très peu d'intérêt à prendre ce genre de précautions. Cependant, c'est presque toujours une bonne idée d'utiliser un style de programmation robuste, par exemple en copiant et en validant les arguments et les valeurs de retour.

9voto

Jerome Points 5318
  1. Oui, ce comportement existe toujours.
  2. Il s'agit d'un risque de sécurité car la classe malveillante pourrait être créée avec autre chose que le javac standard.
  3. Cela dépend de votre degré de paranoïa :) Si vous n'autorisez pas les classes étrangères à s'exécuter dans votre JVM, je ne vois pas le problème. Et si vous le faites, vous avez de plus gros problèmes (bacs à sable et tout).
  4. Je sais que vous n'aviez que 3 questions, mais comme d'autres personnes ici, je pense que c'est une restriction stupide.

9voto

Pete Kirkham Points 32484

Ce comportement existe-t-il encore dans java 5 / 6 ?

Pas exactement comme décrit ; je n'ai jamais vu un compilateur où cela était vrai :

Afin de permettre à cette classe distincte d'accéder aux champs de la classe externe, le compilateur fait passer silencieusement ces champs de la catégorie "private" à la catégorie "package" !

Au lieu de cela, Sun Java 3/4 a créé un accesseur plutôt que de modifier le champ.

Sun Java 6 (javac 1.6.0_16 ) crée un accesseur statique :

public class InnerExample {
    private int field = 42; 

    private class InnerClass {
        public int getField () { return field; };
    }

    private InnerClass getInner () { 
        return new InnerClass();
    }

    public static void main (String...args) {
        System.out.println(new InnerExample().getInner().getField());
    }
}

$ javap -classpath bin -private InnerExample
Compiled from "InnerExample.java"
public class InnerExample extends java.lang.Object{
    private int field;
    public InnerExample();
    private InnerExample$InnerClass getInner();
    public static void main(java.lang.String[]);
    static int access$000(InnerExample);
}

$ javap -classpath bin -c -private InnerExample
static int access$000(InnerExample);
  Code:
   0:   aload_0
   1:   getfield    #1; //Field field:I
   4:   ireturn

S'agit-il réellement d'un risque de sécurité, étant donné que toute classe, autre que les classes externe et interne, qui tenterait d'accéder aux membres privés de la classe externe n'obtiendrait pas de réponse ?

Je spécule un peu ici, mais si vous compilez par rapport à la classe, ce n'est pas le cas, mais si vous ajoutez l'élément access$000 vous pouvez alors compiler le code qui utilise l'accesseur.

import java.lang.reflect.*;

public class InnerThief {
    public static void main (String...args) throws Exception {
        for (Method me : InnerExample.class.getDeclaredMethods()){
            System.out.println(me);
            System.out.printf("%08x\n",me.getModifiers());
        }

        System.out.println(InnerExample.access$000(new InnerExample()));
    }
}

Ce qui est intéressant, c'est que l'accesseur synthétisé possède des drapeaux modificateurs 00001008 où si vous ajoutez une méthode statique au niveau du paquet, elle a des drapeaux 00000008 . Il n'y a rien dans la deuxième édition de la spécification de la JVM pour cette valeur de drapeau, mais cela semble empêcher la méthode d'être vue par javac.

Il semble donc qu'il y ait une fonction de sécurité, mais je ne trouve aucune documentation à ce sujet.

(d'où ce post dans CW, au cas où quelqu'un saurait ce que signifie 0x1000 dans un fichier de classe)

6voto

McDowell Points 62645

Ce comportement existe-t-il encore dans java 5 / 6 ?

Vous pouvez utiliser le javap pour déterminer ce que vos binaires exposent et comment.

package demo;
public class SyntheticAccessors {
  private boolean encapsulatedThing;

  class Inner {
    void doSomething() {
      encapsulatedThing = true;
    }
  }
}

Le code ci-dessus (compilé avec Sun Java 6 javac ) crée ces méthodes dans SyntheticAccessors.class :

Compiled from "SyntheticAccessors.java"
public class demo.SyntheticAccessors extends java.lang.Object{
    public demo.SyntheticAccessors();
    static void access$0(demo.SyntheticAccessors, boolean);
}

Notez la nouvelle access$0 méthode.

4voto

tangens Points 17733

Vous devez réfléchir au type de sécurité que votre application doit offrir. Une application dotée d'une architecture sécurisée ne rencontrera pas ces problèmes.

S'il y a quelque chose qu'un utilisateur n'est pas autorisé à faire avec votre code, vous devez séparer cette fonctionnalité et l'exécuter sur un serveur (où l'utilisateur n'a pas accès aux fichiers de classe).

N'oubliez pas que vous pouvez toujours décompiler les fichiers de classe Java. Et ne vous fiez pas à la "sécurité par l'obscurité". Même un code obscurci peut être analysé, compris et modifié.

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