Security Manager

Gestionnaire de sécurité.

Motivation

Organisation

Java 1.1

Les accès sensibles (écriture de fichiers, de propriétés système, etc.) sont contrôlés via l'appel de méthodes checkXXX() sur le gestionnaire de sécurité. Par exemple l'implémentation de FileOutputStream.write()appelle SecurityManager.checkWrite().

Les méthodes checkXXX() du SecurityManager sont censées lever des exceptions lorsque les accès sont interdits. Le SecurityManager effectue ce contrôle en examinant si la classe appelante a été chargée par un autre ClassLoader que le ClassLoader primordial. Un tel ClassLoader peut par exemple être un AppletClassLoader, ou d'une manière générale impliquer un CODEBASE distant, impliquant des droits limités. Cet examen est effectué via la méthode inClassLoader() du Gestionnaire de Sécurité.

Ainsi,

  • si la classe appelante n'a pas été chargée pas un ClassLoader (autre que celui du système), elle est considérée comme sûres (trusted) et le securityManager lui autorise toutes les actions par défaut. Il n'y a pas pas de granularité, c'est un mode "tout ou rien".
  • la position (profondeur) de ce ClassLoader dans la pile d'exécution (via sa méthode classLoaderDepth()). Ceci permet d'autoriser des classes système sûres (classes système de profondeur faible) à effectuer des opérations sensibles même si ces classes ont été appelées par des classes non sûres (classes utilisateur de grande profondeur).

est représenté par une classe abstraite.

Java 2

Java 2 offre plus de souplesse que le mode tout ou rien en introduisant le concept de permission. Le Gestionnaire de Sécurité n'est plus seul à décider de l'autorisation en fonction du ClassLoader (sanbox ou non), mais délègue la décision à un nouveau composant, le contrôleur d'accès (AccessController). Si l'on reprend notre exemple l'implémentation de FileOutputStream.write() appelle SecurityManager.checkWrite() qui appelle lui-même AccessControl.checkPermission()

On aboutit ainsi à un modèle de "capacité" (permission) à effectuer une opération (Capabilities model) plutôt qu'un modèle d'autorisation (la sandbox autorise ou non) : posséder (accéder à) un objet (directement) donne le droit de l'utiliser : c'est le modèle de l'objet gardé, où l'on demande l'accès à un objet à un gatekeeper. On peut exercer une permission par autorisation d'une autre partie, si l'autre partie nous donne l'objet permission.

on distingue :

  • le Contrôleur d'Accès (AccessController), qui gère les accès à aux différentes ressources, en fonction de la politique de sécurité (security policy) en vigueur.
  • le Gestionnaire de Sécurité (SecurityManager), qui contrôle délègue le contrôle des permissions au Contrôleur d'Accès et gère lui-même le contrôle de quelques accès fondamentaux. Celui-ci :
    • contrôlait les accès aux ressources en fonction
      • des permissions définies par le Contrôleur d'Accès (appliquant la politique de sécurité définie pour la plate-forme). Ces permissions sont, rappellons-le, fonction de l'origine du code appelant.
      • de l'existence ou non d'un ClassLoader dans la pile d'exécution et de la position (profondeur) de ce ClassLoader dans la pile d'exécution, sauf pour :
        • les ClassLoaders système (chargeurs de classes de la plate-forme Java, de ses extensions, ou issues du CLASSPATH)
        • les classes disposant de la permission java.security.AllPermission
        • les blocs de code privilégés
    • offrait un accès dépendant des permissions définies par la politique de sécurité locale
    • n'est plus une classe abstraite, et peut donc être utilisé directement comme Gestionnaire de Sécurité par défaut. Ceci peut être fait en fixant la propriété java.security.manager (java -Djava.security.manager MonApplication) ou par programmation (System.setSecurityManager (new SecurityManager())). Il n'est donc plus nécessaire de développer de classe dérivée du SecurityManager pour personnaliser la politique de sécurité, ce Gestionnaire de Sécurité par défaut exploitant la politique définie par les fichiers prévus à cet effet.

Cependant, afin d'exploiter le nouveau modèle de permissions introduit dans Java 2, divers changements plus ou moins transparents (nous allons voir pourquoi) ont été effectués :Une méthode checkPermission() a été les implémentation de ces méthodes Mais en fait, elles font maintenant toutes appel à la méthode checkPermission() qui se charge d'interroger l'Access Controler.

Tous les accès doivent donc maintenant être validés par le Contrôleur d'Accès, et non plus par le Gestionnaire de Sécurité. Ceci à pour inconvénient de devoir migrer les implémentations de Gestionnaire de Sécurité écrites pour Java 1.1.

Redéfinir les methodes checkXXX() d'un SecurityManager n'est donc plus valide en Java 2, dans la mesure ou cette redéfinition court-circuite l'appel à la vérification des permissions de Java 2. Il est donc nécessaire, soit d'appeler systématiquement super.checkXXX (qui appelera checkPermission) dans les methodes checkXXX() de notre SecurityManager, soit de réécrire le SecurityManager en prenant en compte la délégation du contrôle des accès (permissions) à l'Access Controler. Il est en effet important, d'une manière ou d'une autre, d'exploiter les permissions par défaut fixées dans Java 2, parce que Sun indique qu'elles sont plus sûres et corrigent quelques failles de sécurité de Java 1.

La redéfinition des méthodes checkXXX() peut cependant se révéler toujours utile pour inclure de l'audit ou des affichages GUI lors des tentatives d'actions sensibles.

Exemples

Un exemple d'accès contrôlé est :

Caractéristiques