Home >
Introduction
Create an AIR application based on the loading of external HTML/JS modules that use the sand box bridge in order to communicate with the main application and in order to not lose the AIR’s api capabilities.
This article will explore the security restrictions of the AIR sandboxes and will show a modular and easy way to update the architecture for your AIR applications.
Architecture considerations
An AIR application is delivered with only one AIR file that contains an SWF and some additional files added from developers during the compilation phase, the application folder is usually called an application sandbox and the files stored here have full access to the AIR API (include in this folder only additional files that are really required from the application).
Developers are should really put additional files in the application storage directory both for security reasons and because AIR applications are not allowed to write in the installation folder.
There are several ways to update an AIR application but you have to deeply consider that on Windows on a Mac and on Linux the user needs to have administrative privileges in order to install or update an AIR application.
On Windows, administrators can configure a machine to prevent (or allow) AIR application installation and runtime updates. These settings are contained in the Windows registry under the following key, HKLM\Software\Policies\Adobe\AIR, but if your application needs to be updated frequently you must study determine an update strategy that can work without any conflict with these security restrictions.
One strategy you can implement is to put the content or the applications you need to update in the application storage directory and change the content of these folders downloading the files from the net, but you need to carefully study the security model of AIR. This kind of strategy have to be used carefully because the content stored in the application storage directory is not trusted, a good reason why to use this strategy is that you don’t want that the user has to be the admin for each single update. If you want to provide greater security you have to consider the Adobe Air Update framework http://labs.adobe.com/wiki/index.php/Adobe_AIR_Update_Framework because the AIR update process provides for digital signing of the application to ensure the update that the user receives is the one that the trusted author sent.
Security considerations
When you work with Rich Internet Applications security fails are still dangerous but the application runs in the browser sandbox so it can’t damage the end users machines. Instead, when you are dealing with desktop applications you have to keep in mind that if you miss something form a security point of view, the PC on which the application is running may have serious issues and may be exposed to serious damages.
For instance example an AIR application can be compromised when using external data or content and the following points need to be avoided as much as possible:
- Using data from a network source to determine file name
- Using data from a network source in order to determine paths of installation folders
- Using data from a network source to construct an URL that the application uses to send private information’s
Another security consideration that an AIR developer has to follow is the storage of user credentials. If these data are not securely stored then your application can become a mean for the loss of secure data. When dealing with these kind of data use the EncryptdLocalStore offered by the AIR runtime. Moreover the following are two good starting points when dealing with user credentials or private data:
- Don’t send these data over the net if the destination is not trusted
- Never provide a default password to the application, leave to the user the task to define it otherwise people who already know the default password can address security attacks easily.
Generally the update of an application is provided in order to add new features or in order to fix bugs or security issues. There is a way to perform attacks known as “Downgrade attack” that can be addressed also to an AIR application.
The AIR runtime, when you install an application, by default checks if the application is already installed and makes a comparison between the installed version and the new one. The comparison is performed on the string that represents the version of the application so everybody that knows a security issue on the previous version of your application can release a holder version with a new version number. In order to avoid this issue it is a good practice to make the version checking in the source code of the application against a version file stored on the net, in this way you can guarantee that the correct version is installed on the user machine.
When an application is installed, all the files included in the AIR installer are installed into an application directory on the user's computer. These files are assigned to the application sandbox when the application is run. This means that they have full security privileges, including interaction with the local file system.
To protect Adobe AIR applications from accidental leakage of privileged information or control, the following restrictions are placed on content in the application security sandbox:
- Code in the application security sandbox cannot allow cross-scripting to other sandboxes by calling the Security.allowDomain() method. Calling this method from the application security sandbox has no effect.
- Importing non application content into the application sandbox by setting the LoaderContext.securityDomain or the LoaderContext.applicationDomain property is prevented
Some packages that contain API available only to content in the application sandbox are as follows; some classes of these packages can’t be used from a sandbox that differs from the application one
- flash.desktop.*: Clipboard, NativeApplication, NativeDragManager, NativeWindow, and so on
- flash.data.*: EncryptedLocalStore, SQLConnection, SQLMode, and so on
- flash.filesystem.*: FileStream, FileMode, and File
The AIR security model of sandboxes is composed by the ones you find in the Flash Player with the addition of the application sandbox. There are several restrictions to consider when your application uses external content loaded from the net or stored in the application storage directory.
JavaScript code in a non-application sandbox does not have access to the window.runtime object, and as such this code cannot execute the AIR API that access to the file system, to the clipboard, etc., the XMLHttpRequest cannot be used from an HTML application loaded in an frame or iframe if the allowCrossdomainXHR attribute in the containing frame or iframe is not explicitly set. There are restrictions on calling the JavaScript window.open() method, restrictions in the use of the eval() function or more in general imitations on using APIs that can dynamically transform strings into executable code after the code is loaded.
The window.open() JavaScript function can be called from content that is not in the application sandbox security only in response to an user input (Mouse, KeyBoard) and cannot be delayed via setTimeOut(). Content stored in the remote (network) sandboxes can only use the window.open() method to open content in remote network sandboxes and not in order to open content from the application or local sandboxes. On the other side Content in the local-with-filesystem can use the window.open() function only in order to open local content.
When an HTML pages is loaded in an AIR application, after the onload body event is fired and the onLoad event handler has been executed, AIR security model applies a lot of restrictions to the use of dynamically generated code in order to prevent malicious attacks to the application.
There are several ways to generate dynamic code, the most diffused are:
- The eval() function
- The innerHTML properties
- Set the src attribute for a script tags to load a JavaScript file that is outside of the application directory
- Use the setInterval() or setTimout() function where the first parameter defines a function to run asynchronously with a string
- Use the document.write() or document.writeIn() functions
The AIR security model allows the use of these techniques when the page is loading but after the events the use of these dynamic techniques is very restricted.
Consider for instance the eval() function that after the page is loaded in the AIR application it loses a wide range of its powerful functionalities like functions invocation, functions declarations, properties settings and so on.
Moreover JavaScript code in a non-application sandbox does not have access to the window.runtime object, and as such this code cannot execute AIR APIs, keep in mind that the restrictions on eval do not apply to the non-application sandbox.
All these restrictions are useful for a secure implementation of an AIR application but sometimes development requires a more flexible architecture and AIR gives us the possibility to scripting between content in different domains through the sandbox bridge.
Sandbox bridge
In certain circumstances an AIR application needs to provide trusted access to the AIR api and to other applications loaded from another domain or stored in the application storage directory.
To accomplish this, the runtime provides a way to define a gateway between different sandboxes, the sandbox bridge can provide explicit interaction between different application security sandboxes.
When you load external content in your AIR application there are two properties that can handle the communication between different domains. The parentSandboxBridge lets the external content to get a reference to properties and methods of the loading application, the childSandboxBridge instead lets loaded content expose properties and function to scripts in the loading content.
Objects exposed via the sandbox bridge are passed by value, not by reference and all data is serialized. This means that the objects exposed by one side of the bridge cannot be set by the other side, and that objects exposed are all untyped.
In an HTML frame or iframe the parentSandboxBridge and the childSandboxBridge properties are added to the window object of the child document so the following example shows how a script running in a child document can expose an object containing a function and a property to its parent:
var obj = {};
obj.sayHello = function(){
return "Hello world!";
}
obj.userName = "Giorgio"
window.childSandboxBridge = obj;
If this child content was loaded into an iframe assigned an id of “plugin”, you could access the interface from parent content by reading the childSandboxBridge property of the frame:
var childInterface = document.getElementById("plugin").contentWindow.childSandboxBridge;
air.trace(childInterface.userName);
air.trace(childInterface.sayHello());
In order to easily use the AIR API from the JavaScript inside an HTML application it is mandatory to include the JavaScript file AIRAliases.js that contains a mapping to the AIR API.
<script src="AIRAliases.js"></script>
The file acts like a bridge to make your life easier when you are dealing with the File, nativeWindow, ClipBoard, etc. functionalities of AIR
// file
air.File = window.runtime.flash.filesystem.File;
air.FileStream = window.runtime.flash.filesystem.FileStream;
air.FileMode = window.runtime.flash.filesystem.FileMode;
It’s the responsibility of the developer to expose the API in a secure way. What I mean is that it’s very dangerous to put as an argument the path to use to write a file or other sensitive information that can permit to malicious attacks to write on specific locations or access different zones of the client directly.
It’s more secure to have an API that is verbose rather than an API that grants access to the file system without any control
readFile(path:String); // unsecure
readUserPreference(); // secure
So the sandbox bridge is the only way to grant access to the AIR api to content that is loaded from a non application sandbox context.
Keep in mind that by default the SWF files loaded from a different context needs to be allowed to execute the code stored inside themselves and that the HTMLLoader.loadString(str:String) method provides you a useful way to create an agile architecture in which HTML applications can use the AIR API. Content loaded via the loadString() method is put in the application security sandbox, giving it full access to AIR APIs. Since the release of AIR 1.5 the loadString() method support an additional argument, a flag that lets the user choose in which sandbox the content will be loaded.
HTML based application architecture
Developers who come from an HTML background are used to creating applications that use frames becaue they provide a means both for maintaining data persistence and for working securely with remote content.
When you are planning to work with remote content or content loaded from a different sandbox it is a good idea to put the HTML page that contains the frame in the application storage directory and then load the content in the frames. If you don’t want to use the data persistence API provided by AIR you can simply make a porting of your application, but it’s more convenient to use SharedObject, EncryptedLocalStorage, etc. to keep the data live during the execution of the application and grant the access to the API you need as shown before.
The loadString() method can provide to you access to the AIR API without any restrictions but if you load content from a remote web site you are seriously exposing the application to attacks.
As you know, AIR provides support to local databases through SQLite. A good architecture that can keep the application secure, easy to maintain and updatable may be to store the HTML applications inside a local database and load each plugin trough the loadString() method, the update can be performed on the database and you can keep your applications more secure.
Create an updatable application
Let’s start by examining a small sample of an AIR application that consumes the services of an existing AJAX application and that keeps the update capabilities safe without the administrative privileges we described in the first part of the article.
I don’t like to reinvent the wheel in order to fit the purpose of the article so I got an existing AJAX image gallery by Jens Boje to get some images from flickr and I have made some small changes in order to use it with AIR. The sample application browses some images from flickr and give to the user the possibility of adding them to a basket in order to download the images on his desktop.
The structure of the application is the one you find in the following image
Image 1
The FlickrImageGallery.mxml file is the one that defines the UI elements of the application, it contains a ViewStack with two Panel. In the first Panel you find an HTML component that loads the AJAX application, in the second one you find a TileList that shows the selected images. The navigation between the two Panel is granted by a ToggleButtonBar instance
<mx:ToggleButtonBar id="stepControl" dataProvider="steps" width="500" selectedIndex="0" />
<mx:ViewStack id="steps" width="500" height="500">
<mx:Panel id="browseYourPhoto" title="Browse pictures on flickr" label="Step 1" width="100%" height="100%" layout="vertical" horizontalAlign="center" paddingLeft="10" paddingRight="10" paddingTop="10" paddingBottom="10">
<mx:HTML complete="htmlLoaded(event)" width="100%" height="100%" verticalScrollPolicy="off" horizontalScrollPolicy="off" location="plugincontainer.htm" />
</mx:Panel>
<mx:Panel id="showResults" title="Save selected pictures" label="Step 2" width="100%" height="100%" paddingLeft="5" paddingRight="5" paddingTop="5" paddingBottom="5">
<mx:TileList width="100%" height="100%" dataProvider="{imagesBasket}" itemRenderer="FlickrImage" />
<mx:Button label="save pictures" height="100%" width="100%" icon="@Embed(source='../assets/save.png')"/>
</mx:Panel>
</mx:ViewStack>
The location property of the HTML component is the one you have to use in order to load the content in the component and its value is the file plugincontainer.htm that is stored in the application directory and that therefore grants full access to the AIR api.
I said that the application needs to be updatable and so the plugincontainer.htm acts only as a wrapper to the plugin stored in the application directory. This way future updates may be executed, to avoid having to grant administrative privileges to the user.
The HTML file has to load another HTML file that is stored in the application storage directory when the application start, the file is stored with all the necessary JavaScript files in the plugin directory
This operation and the initialization of the ArrayCollection we need in order to store the selected images are performed in the init() method that is defines has a listener for the creationComplete event
private function init():void{
imagesBasket = new ArrayCollection();
var file:File = File.applicationStorageDirectory.resolvePath(PLUGIN_DIRECTORY);
if(!file.exists){
var sourceDir:File = File.applicationDirectory.resolvePath(PLUGIN_DIRECTORY);
sourceDir.copyTo(file, true);
}
}
<html>
<head>
</head>
<body>
<iframe id="sandbox" height='100%' width='100%' frameBorder="0" scrolling="no"
src="plugins/flickr/sample_gallery_with_moopix.htm"
sandboxRoot="http://flickr/"
documentRoot="app-storage:/"
/>
</body>
</html>
The reason why I haven’t used as the sandboxRoot the fully qualified domain name for flickr domain is that the images are store in URL like this one http://farm{farm-id}.static.flickr.com/{server-id}/{id}_{secret}.jpg.
In order to load the sample_gallery_with_moopix.htm that contains the plugin which is copied in the application storage directory during the initialization of the application, I set the documentRoot attribute that is supported from the iframe tag in the AIR runtime.
The data validation of the file path is performed directly in the JavaScript file used in order to create the gallery.
Between the JavaScript files that are shipped with the Jens Boje’s galley there is the imago.js file, the only modification I did in this file is to add an handler for the click event on the images I need in order to communicate with AIR. The modification was in the layout method of the Gallery class
document.getElementById('imagoCurrentImageDiv').onclick = addToBasket;
At the end of the class I defined the addToBasket function that get the URL of the image and startup the communication with AIR calling a function that we have to define in the plugincontainer.htm file
function addToBasket(){
var e = document.getElementById('imagoCurrentImageDiv').childNodes[2].src;
window.parentSandboxBridge.addImage(e);
alert("The image " + e + " has been added!")
}
If you test the application now you’ll get an error because the parentSandboxBridge has not be defined, so go back to the plugincontainer.htm file and add some scripts in the HEAD tag and define the ondominitialize event handler
<html>
<head>
<script>
var bridgeInterface = {};
bridgeInterface.addImage = function (fl){
var clipBoard = air.Clipboard;
clipBoard.generalClipboard.clear();
clipBoard.generalClipboard.setData(air.ClipboardFormats.TEXT_FORMAT, fl);
imagesBasket.addItem({filePath: fl});
}
function engageBridge(){
document.getElementById("sandbox").contentWindow.parentSandboxBridge = bridgeInterface;
}
</script>
<script src="AIRAliases.js" mce_src="AIRAliases.js" type="text/javascript"></script>
</head>
<body>
<iframe id="sandbox" height='100%' width='100%' frameBorder="0" scrolling="no"
src="plugins/flickr/sample_gallery_with_moopix.htm"
sandboxRoot="http://flickr/"
documentRoot="app-storage:/"
ondominitialize="engageBridge()"/>
</body>
</html>
The engageBridge method is the one that sets the parentSandboxBridge property in order to grant access to the addImage function that is responsible for copying the selected image path in the clipboard and to add the element to the ArrayCollection that we defined in the AIR application.
In order to complete the communication bridge between the HTML file and AIR we need to define the imagesBasket also in the plugincontainer.htm file. In order to do this you have to define the complete event handler for the HTML component used in AIR
private function htmlLoaded(e:Event):void{
var html:HTMLLoader = (e.target as HTML).htmlLoader
html.window.imagesBasket = imagesBasket;
}
It gets a reference the htmlLoader property and store a reference to the imagesBasket ArrayCollection used by the TileList.
Run the application, now you are able to get all the power of the AIR api in a secure and updatable environment without exposing the user’s desktop to malicious attacks or code injections.




Facebook Application Development
I don't understand this article at all. You talk about being able to create an updatable AIR app that doesn't require admin privileges, yet in your code and examples you mention nothing of the air.update.ApplicationUpdaterUI framework, nor update() code.
How does this work? How does your code call the update function and actually update the application without having to be logged in as an admin?
I don't understand this article at all. You talk about being able to create an updatable AIR app that doesn't require admin privileges, yet in your code and examples you mention nothing of the air.update.ApplicationUpdaterUI framework, nor update() code.
How does this work? How does your code call the update function and actually update the application without having to be logged in as an admin?
Hi,
Thanks for the comment, the ApplicationUpdaterUI framework need that the user has the privileges to install the appllication, my code simply can download some html or flash based plugin in the app:/ directory.
Hey Giorgio.
I have only skimmed the article thus far, but I would be interested in seeing some of the code in action. Assuming that you're creating both an AIR project and a Flex project with a thoroughly edited html-template directory (in the flex proj), would you mind zipping the two projects and posting them as an example?
Thanks for the great article (well... I'm assuming since I haven't totally read it yet... but it looks great!).
Talk to you soon,
Jeremy