Autoriser un acteur donné à effectuer une opération sur une ressource.
A l'image de la hiérarchie des exceptions, les permissions sont représentées par une classe racine, java.security.Permission
(classe abstraite à ne pas confondre à l'interface obsolète java.security.acl.Permission
), et
diverses classes dérivées représentant les permissions prédéfinies de la plate-forme Java 2. Comme pour les
exceptions également, il est possible de créer ses propres permissions au travers de classes dérivant de cette
racine ou de l'une des permissions prédéfinies.
Il existe effectivement des permissions relatives à chaque aspects de la plate-forme Java (système de fichiers, réseau, interface graphique, introspection...) dont les classes se trouvent dans les packages concernés (java.io, java.net, java.awt, java.lang.reflect...). On peut trouver une liste complète des permissions possibles et des risques encourus à les autoriser dans la documentation du JDK.
Comment fixer des permissions ?
Il est possible de fixer des permissions de deux manières :
Notons toutefois que la programmation sera dans certains cas à préférer aux fichiers de politique de sécurité, dans la mesure où certains types de permissions sont dépendantes de la plate-forme, comme les permissions liées au système de fichier, dont le paramétrage peut avoir à spécifier des séparateurs de fichiers spécifiques ("/" sous Unix, "\" sous Windows, ":" sous MacOS...).
Concrètement, les permissions sont définies par :
FilePermission
par exemple)Une permission définie par ces trois composantes peut ensuite être accordée à un acteur identifié par son origine de code, qui encapsule l'URL de la provenance du code (y compris pour du code local) et/ou un certificat (il est donc possible de définir des permissions pour des acteurs ne disposant pas de certificat).
Toute application Java (y compris les applets) aura toujours le droit de lire le système de fichier à partir du niveau défini par l'URL de leur origine de code (sous-répertoires y compris donc). Elle n'aura jamais besoin d'une quelconque permission pour cela. Ne placez donc pas de ressources sensibles (données confidentielles, exécutables) dans le répertoire d'origine ou sous le répertoire d'origine d'un code suspect.
Par exemple :
<strong>grant codeBase</strong> "file:${java.home}/lib/ext/-" {<br> <strong>permission
java.security.AllPermission</strong>;<br> };
autorise toutes les entités chargées à partir de l'URL file:${java.home}/lib/ext
à effectuer toutes les
actions possibles, et :
<strong>grant signedBy</strong> "Duke", <strong>codeBase</strong> "https://buwin/plugintest"
{<br> <strong>permission</strong> <strong>java.io.FilePermission</strong> "tmpFoo", "write";<br> };
autorise tout code signé par Duke et provenant de l'URL https://buwin/plugintest
à écrire le fichier
tmpFoo
.
Chaque politique peut être associée aux opérations d'une plate-forme Java (JDK ou JRE/plugin), ainsi qu'à un utilisateur particulier de cette plate-forme.
Cette politique est définie dans un fichier, spécifié par le fichier de sécurité de Java 2,
java.security
dans le sous-répertoire lib/security/
de votre plate-forme Java.
On trouvera par défaut dans ce fichier que la spécification de la politique de sécurité doit être lue depuis le
fichier java.policy
dans le même sous-répertoire, ainsi que dans le répertoire HOME de l'utilisateur
courant :
policy.url.1=file:${java.home}/lib/security/java.policy<br>
policy.url.2=file:${user.home}/.java.policy
Certificate, X509Certificate, CertificateFactory
Le rôle du contrôleur d'accès est donc à terme de remplacer le Gestionnaire de Sécurité. Il s'agit d'une classe statique (non-instantiable), contrôlant les accès aux ressources en fonction du Contexte de Sécurité courant.
Il est possible pour une classe privilégiée (disposant d'une permission donnée) de transmettre momentanément son contexte de protection (les permissions qui lui sont accordées) à une autre classe non privilégiée.
Il convient cependant d'être particulièrement précautionneux quant aux opérations effectuées dans de telles zones temporairement privilégiées (attention à ce que l'on fait, ce que l'on permet de faire) et d'une manière générale d'y effectuer le minimum possible d'opérations.
(AccessControlContext / thread) pour examen d'une permission dans un autre contexte : rapport avec le GuardedObject ?
Un domaine de protection représente un modèle similaire à celui de la sanbox de Java 1.1, où l'identification par CODEBASE serait étendue au concept d'origine de code.
Un domaine de protection associe un ensemble de permissions à une origine de code donnée.
Il existe typiquement deux catégories de domaines de protection :
L'exécution du code de plusieurs classes par un même thread implique donc la traversée de plusieurs domaines de protection. On définit le Contexte de Sécurité comme l'intersection des permissions accordées par les Domaines de Protection traversés.
Une classe autorisée à écrire des fichiers pourra par exemple appeler une classe non autorisée à écrire de tels fichiers. Dans ce cas, la règle de l'intersection des domaines de protection ne permettra pas au code de la classe non privilégiée de disposer des privilèges de la classe appelante (l'intersection correspond à la permission la plus faible des deux).
Il est possible à tout moment de demander au Contrôleur d'Accès quel est le Contexte de Sécurité courant (du
thread courant), via la méthode statique AccessControler.getContext()
. Cette méthode retourne un
objet de type java.security.AccessControlContext
ou l'un de ces descendants (il pourra en effet être
intéressant de spécialiser le contexte de sécurité pour y inclure de nouveaux paramètres).
Il peut arriver qu'une classe privilégiée souhaite transmettre temporairement des privilèges à une classe moins privilégiée. Cette opération s'effectue au travers de l'exécution d'un "bloc privilégié".
L'exécution d'un Bloc Privilégié consiste à demander l'exécution d'une Action Privilégiée. Une Action Privilégiée
est une classe contenant le code devant disposer de privilèges. Une telle classe doit implémenter l'interface
java.security.PrivilegedAction
, qui impose de fournir le code d'une méthode run()
, où
doit se trouver le code privilégié.
Par exemple :
class PrivilegedUserNameAccess implements <strong>java.security.PrivilegedAction</strong> {<br>
public Object run() {<br> return System.getProperty (<span class="codeString">"user.name"</span>); <br> }<br> }
L'exécution de cette action privilégiée est alors demandée au Contrôleur d'Accès :
String userName = (String) <strong>AccessControler.doPrivileged</strong> (new
PrivilegedUserNameAccess());
Dans cet exemple, le bloc permet de retourner la valeur d'une propriété qui ne serait pas accessible si la classe
appelante ne disposais pas de la permission nécessaire (en l'occurence java.util.PropertyPermission
("user.name", "read")
).
Cependant, il est possible de faire bénéficier une action privilégiée d'un contexte de sécurité différent de
celui de la classe appelante (un contexte plus limité par exemple). Dans ce cas, il suffit de fournir à la méthode
doPrivileged
() le contexte adéquat :
String userName = (String) <strong>AccessControler.doPrivileged</strong> (new
PrivilegedUserNameAccess(), someActionContext);