Programmation croisée du contenu dans des sandbox de sécurité distincts

Adobe AIR 1.0 et les versions ultérieures

Le modèle de sécurité du moteur d’exécution isole le code de différentes origines. La programmation croisée du contenu dans des sandbox de sécurité distincts permet au contenu d’un sandbox de sécurité d’accéder à des propriétés et à des méthodes sélectionnées dans un autre.

Sandbox de sécurité AIR et code JavaScript

AIR applique une politique d’origine commune qui empêche le code d’un domaine d’interagir avec le contenu d’un autre. Tous les fichiers sont placés dans un sandbox sur la base de leur origine. Habituellement, le contenu d’un sandbox d’application ne peut pas enfreindre le principe d’origine commune et inter-coder le contenu chargé depuis un emplacement qui se trouve hors du répertoire d’installation de l’application. Toutefois, AIR met à votre disposition quelques techniques qui vous permettent d’inter-coder du contenu hors application.

L’une d’entre elles utilise des images ou des iframes pour mapper le contenu de l’application dans un sandbox de sécurité distinct. Toute page chargée à partir de la zone à sandbox de l’application se comporte comme si elle l’avait été à partir du domaine distant. Par exemple, le mappage du contenu de l’application sur le domaine example.com lui permettrait d’inter-coder des pages chargées à partir d’example.com.

Comme cette technique place le contenu de l’application dans un sandbox distinct, le code au sein de ce contenu n’est non plus assujetti aux restrictions qui entourent l’exécution du code des chaînes évaluées. Vous pouvez utiliser la technique de mappage des sandbox pour assouplir ces restrictions, même s’il n’est pas nécessaire d’inter-coder un contenu distant. Le mappage de contenu de cette façon est particulièrement utile lorsque vous travaillez avec l’une des nombreuses structures de JavaScript ou avec du code existant qui se fonde sur l’évaluation des chaînes. Toutefois, vous devriez tenir compte d’un risque supplémentaire et vous en prémunir : celui d’un contenu non approuvé qui pourrait être introduit et exécuté lorsque le contenu tourne hors du sandbox de l’application.

Cependant, un contenu de l’application mappé sur un autre sandbox perd son droit d’accès à des interfaces de programmation d’AIR de telle sorte que la technique de mappage du sandbox ne peut pas être utilisée pour présenter des fonctionnalités d’AIR à du code exécuté hors du sandbox de l’application.

Une autre technique de programmation croisée vous permet de créer une interface, appelée pont de sandbox, qui sert de passerelle entre le contenu d’un sandbox hors application et son document parent dans le sandbox de l’application. Le pont permet au contenu enfant d’accéder à des propriétés et à des méthodes définies par le parent et inversement, ou bien les deux à la fois.

Enfin, vous pouvez lancer des XMLHttpRequests entre les domaines à partir du sandbox de l’application et, facultativement, d’autres sandbox.

Pour plus d’informations, voir Eléments image et iframe HTML, Sécurité HTML dans Adobe AIR et Objet XMLHttpRequest.

Chargement du contenu de l’application dans un sandbox hors application

Pour permettre à un contenu de l’application d’inter-coder sans risque un contenu chargé à partir d’un emplacement se trouvant hors du répertoire d’installation de l’application, vous pouvez utiliser l’élément image ou iframe pour charger ce contenu en tant que contenu externe dans le même sandbox de sécurité. Si vous ne voulez pas inter-coder de contenu distant mais que vous souhaitez charger une page de votre application hors du sandbox de l’application, vous pouvez utiliser la même technique, c’est-à-dire spécifier http://localhost/ ou toute autre valeur inoffensive comme domaine d’origine.

AIR ajoute les nouveaux attributs, sandboxRoot et documentRoot à l’élément d’image pour vous permettre de spécifier si un fichier d’application chargé dans l’image devrait être mappé sur un sandbox hors application. Les fichiers dont la résolution aboutit à un chemin sous l’URL sandboxRoot sont plutôt chargés à partir du répertoire documentRoot. Pour des raisons de sécurité, le contenu de l’application chargé ainsi est traité comme s’il avait été chargé à partir de l’URL de sandboxRoot.

La propriété sandboxRoot spécifie l’URL à utiliser pour déterminer le sandbox et le domaine dans lesquels placer le contenu de l’image. Les modèles d’URL file:, http: ou https: doivent être utilisés. Si vous spécifiez une URL relative, le contenu reste dans le sandbox de l’application.

La propriété documentRoot spécifie le répertoire à partir duquel il faut charger le contenu de l’image. Les modèles d’URL app:, http: ou app-storage: doivent être utilisés.

L’exemple ci-dessous mappe le contenu installé dans le sous-répertoire sandbox de l’application qui doit s’exécuter dans le sandbox distant et le domaine www.example.com :

<iframe 
    src="http://www.example.com/local/ui.html"  
    sandboxRoot="http://www.example.com/local/"  
    documentRoot="app:/sandbox/"> 
</iframe>

La page ui.html pourrait charger un fichier JavaScript à partir du dossier sandbox local à l’aide de la balise de script suivante :

<script src="http://www.example.com/local/ui.js"></script>

Elle pourrait également charger un contenu à partir d’un répertoire sur le serveur distant à l’aide d’une balise de script telle que la suivante :

<script src="http://www.example.com/remote/remote.js"></script>

L’URL sandboxRoot masquera tout contenu sur la même URL du serveur distant. Dans l’exemple ci-dessus, vous ne pourriez pas avoir accès à un contenu distant, quel qu’il soit, à l’adresse www.example.com/local/ ou même à l’un de ses sous-répertoires, car AIR remappe la requête sur le répertoire local de l’application. Les requêtes sont remappées, qu’elles proviennent d’une navigation de pages, d’une XMLHttpRequest ou de tout autre moyen de chargement de contenu.

Configuration d’une interface de pont de sandbox

Vous pouvez utiliser un pont de sandbox lorsqu’un contenu dans le sandbox de l’application doit accéder à des propriétés ou des méthodes définies par un contenu dans un sandbox hors application ou encore lorsqu’un contenu hors application doit accéder à des propriétés et des méthodes définies par un contenu dans le sandbox de l’application. Créez un pont avec les propriétés childSandboxBridge et parentSandboxBridge de l’objet window de tout document enfant.

Création d’un pont de sandbox enfant

La propriété childSandboxBridge permet au document enfant de présenter une interface au contenu dans le document parent. Pour présenter une interface, vous définissez la propriété childSandbox sur une fonction ou un objet dans le document enfant. Vous pouvez alors accéder à l’objet ou la fonction à partir du contenu dans le document parent. L’exemple ci-dessous montre comment un script qui s’exécute dans un document enfant peut présenter un objet contenant une fonction ou une propriété à son parent :

var interface = {}; 
interface.calculatePrice = function(){ 
    return ".45 cents"; 
} 
interface.storeID = "abc" 
window.childSandboxBridge = interface;

Si cet enfant a été chargé dans une iframe avec une id "child", vous pourriez accéder à l’interface à partir du contenu parent en lisant la propriété childSandboxBridge de l’image :

var childInterface = document.getElementById("child").contentWindow.childSandboxBridge; 
air.trace(childInterface.calculatePrice()); //traces ".45 cents" 
air.trace(childInterface.storeID)); //traces "abc"

Création d’un pont de sandbox parent

La propriété parentSandboxBridge permet au document parent de présenter une interface au contenu dans le document enfant. Pour présenter une interface, le document parent définit la propriété parentSandbox du document enfant sur une fonction ou un objet spécifié dans le document parent. Vous pouvez alors accéder à l’objet ou à la fonction à partir du contenu dans le document enfant. L’exemple ci-dessous montre comment un script qui s’exécute dans une image parent peut présenter à un document enfant un objet contenant une fonction :

var interface = {}; 
interface.save = function(text){ 
    var saveFile = air.File("app-storage:/save.txt"); 
    //write text to file 
} 
document.getElementById("child").contentWindow.parentSandboxBridge = interface;

A l’aide de cette interface, un contenu dans l’image enfant pourrait enregistrer du texte dans un fichier appelé save.txt mais il n’aurait aucun autre accès au système de fichiers. Le contenu enfant pourrait appeler la fonction save comme suit :

var textToSave = "A string."; 
window.parentSandboxBridge.save(textToSave);

Le contenu de l’application devrait présenter l’interface la plus étroite possible aux autres sandbox. Un contenu hors application devrait être considéré comme douteux par nature étant donné qu’il peut être sujet à une introduction de code accidentel ou malveillant. Vous devez mettre en place des mesures de protection appropriées pour éviter un usage abusif de l’interface que vous présentez par le pont de sandbox parent.

Accès à un pont de sandbox parent lors du chargement d’une page

Pour qu’un script dans un document enfant accède à un pont de sandbox parent, le pont doit être paramétré avant l’exécution du script. Les objets fenêtre, image et iframe distribuent un événement dominitialize lorsqu’une page DOM est créée, mais avant qu’un script ne soit analysé ou des éléments DOM ajoutés. Vous pouvez utiliser l’événement dominitialize pour établir le pont suffisamment tôt dans la séquence de création de la page afin que tous les scripts du document enfant puissent y accéder.

L’exemple ci-dessous montre comment créer un pont de sandbox parent en réponse à l’événement dominitialize distribué à partir de l’image enfant :

<html> 
<head> 
<script> 
var bridgeInterface = {}; 
bridgeInterface.testProperty = "Bridge engaged"; 
function engageBridge(){ 
    document.getElementById("sandbox").contentWindow.parentSandboxBridge = bridgeInterface; 
} 
</script> 
</head> 
<body> 
<iframe id="sandbox" 
            src="http://www.example.com/air/child.html"  
            documentRoot="app:/" 
            sandboxRoot="http://www.example.com/air/" 
            ondominitialize="engageBridge()"/> 
</body> 
</html>

Le document child.html suivant montre comment un contenu enfant peut accéder au pont de sandbox parent :

<html> 
    <head> 
        <script> 
            document.write(window.parentSandboxBridge.testProperty); 
        </script>   
    </head>   
    <body></body> 
</html>

Pour écouter l’événement dominitialize sur une fenêtre enfant plutôt qu’une image, il vous faut ajouter l’écouteur à l’objet window enfant créé par la fonctionwindow.open() :

var childWindow = window.open(); 
childWindow.addEventListener("dominitialize", engageBridge()); 
childWindow.document.location = "http://www.example.com/air/child.html";

Il n’y a alors pas moyen de mapper le contenu de l’application dans un sandbox hors application. Cette technique n’est utile que lors du chargement de child.html hors du répertoire de l’application. Vous pouvez néanmoins mapper un contenu de l’application dans un sandbox hors application, mais il vous faut d’abord charger une page intermédiaire, qui elle-même utilise des images, afin de charger le document enfant et le mapper dans le sandbox souhaité.

Si vous utilisez la fonction createRootWindow() de la classe HTMLLoader pour créer une fenêtre, celle-ci n’est pas un enfant du document à partir duquel createRootWindow() est appelée. Ainsi, il n’est pas possible de créer un pont de sandbox à partir de la fenêtre appelante dans un contenu hors application chargé dans la nouvelle fenêtre. Il faut plutôt charger une page intermédiaire dans la nouvelle fenêtre qui elle-même utilise des images pour charger le document enfant. Vous pouvez alors établir le pont à partir du document parent de la nouvelle fenêtre avec le document enfant chargé dans l’image.