Introduction
Il peut arriver que vous vouliez exécuter du code JavaScript avec des droits restreints. Depuis Firefox 1.5 (Gecko 1.8) vous pouvez faire cela en utilisant une API qui permet la création de "bacs à sables" à l'intérieur desquels le code sera interprété avec des droits restreints, comme le code exécuté dans une page web.
Usage
Il faut créer un objet au moyen du constructeur Components.utils.Sandbox
, pour pouvoir ensuite utiliser la fonction evalInSandbox()
. L'objet doit être initialisé soit avec une URI, soit, et c'est préférable, avec un objet DOM window
(nsIDOMWindow
). Depuis Firefox 3, il est également possible de l'initialiser avec un nsIPrincipal
. Qu'il s'agisse d'une URI, d'une nsIDOMWindow
ou d'un nsIPrincipal,
l'objet utilisé pour initialiser le bac à sable, servira d'origine pour le code interprété. Cette origine sera par exemple utilisée pour les contrôles de sécurité basés sur la "même origine". Par exemple, si le bac à sable est initialisé avec l'URI https://www.example.com/
, les appels AJAX exécutés à l'intérieur seront limités au serveur https://www.example.com. Si le code interprété modifie la propriété document.domain
, ce qui à un impact sur les contrôles de même origine, il faut initialiser le bac à sable avec une nsIDOMWindow
ou un nsIPrincipal
Voici un exemple avec une URI:
// crée un bac à sable en précisant l'URI d'origine var s = Components.utils.Sandbox("https://www.example.com/");
Le bac à sable n'est qu'un objet JavaScript vide marqué comme créé avec des droits restreints. Il deviendra le contexte global d'exécution du code interprété au moyen de evalInSandbox(text, sandbox)
. En ajoutant des propriétés à l'objet, elles seront accessibles dans ce contexte. Ainsi, en utilisant l'exemple précédent:
s.y = 5; // ajoute la propriété 'y' valant 5 dans le contexte global du bac à sable var result = Components.utils.evalInSandbox("x = y + 2; x + 3", s); // le résultat vaut 10 et s.x 7
Les manipulations sur les objets mis à disposition dans le bac à sable ne transmettent pas de droits:
s.foo = Components; // l'instruction suivante déclenchera une erreur "Opération interdite" Components.utils.evalInSandbox("foo.classes", s);
Par contre, toute fonction réussissant à "sortir" du bac à sable s'exécutera avec les mêmes droits que le code dans le chrome. La section suivante décrit plusieurs situation où cela peut se produire.
Note: du fait du bug 350558, les tableaux créés à l'intérieur du bac à sable ne sont pas des instance de l'objet Array
Sécurité
evalInSandbox()
si vous accédez aux propriétés d'un objet créé dans le bac à sable.Exemples:
<script src="prototype.js"></script> <script> var s = new Components.utils.Sandbox(url); var x = Components.utils.evalInSandbox(untrusted_code, s); if (x == 1) { /* ce code n'est pas sûr car il fait appel à la fonction x.valueOf() */ } if (x === 1) { /* ce code est sûr */ } /* les deux assignations suivantes ne sont pas sûres */ var y = x.y; var z = sandbox.z; if (typeof x == "number") { /* sûr */ } </script>
Lorsque le code (x == 1)
est exécuté, la méthode x.valueOf()
est appelée depuis le chrome, ce qui peut permettre à du code non sûr de créer un objet String
dans le contexte global du chrome.
Si du code dans le Chrome a ajouté une fonction privilégiée au prototype des objets String
, Object
, ou Function
, du code non sûr pourrait utiliser cette fonction et s'en servir pour s'exécuter avec les droits du chrome.
Pour éviter ce risque, il faut soit éviter d'utiliser des objets créés par evalInSandbox()
de quelque manière qui pourrait faire qu'une fonction de cet objet serait appelée, ou vous devez vous assurer avant d'appeler evalInSandbox()
pour exécuter une chaîne contenant un objet au format JSON que celle-ci ne contient aucune fonction.
Pour en savoir plus: PiggyBank analysis of sandbox