Remote Plugins and Modules in AIR

I’ve been getting a lot of questions about how to use remote “modules” in AIR. “Modules” is in quotes because it can mean different things. In every case, it refers to running some SWF or HTML/JS content that is loaded at runtime from the network. The difference are in how the content is loaded and how an application can communicate with it.
Depending on the specifics of the modules you want to load there are different options about how to load and communicate with the content. Let’s explore the options!!


In web browsers, we run remote content all the time, and don’t have to worry about security. Images and pages from other domains can execute their own code, but can’t reach into ours. We can also choose to pull someone else’s code into our own and run it. Dynamic evaluation of Javascript is a powerful tool, and it can be elegantly used. SWF has the same security model: remote content (images, audio, video, SWF) can run without accessing our code, and we can choose to import remote code and content (loadBytes()).
Privileged local applications have a responsibility to protect their users. No one wants their app to have flaws that expose their users’ data or computers to attackers. AIR has strict but simple standards about which content (SWF, JS) can get application privileges. Only the files in the application’s security sandbox may use the local File APIs, Windowing APIs, etc. Files will be in this sandbox only if they are loaded from the application’s local directory via an app: URI, or are imported into that sandbox using explicit APIs.

Separate Sandboxes

SandboxedLoading.png
If you don’t trust the content, or don’t want to verify that it really is trusted, you can keep it at arm’s length. This is the default; remote content is loaded in a remote sandbox according to its domain. One difference in AIR is key: Communication is possible across sandboxes at runtime. Using the SandboxBridge API in AIR, content can expose a discoverable API to content across the border. Any data passed is copied to its destination. Complex data structures can be passed across the (deep-copy, without functions and custom class information).
This feels more like service-style communication. Loading a file is like getting a service endpoint. Your code’s integrity is protected by the loose coupling, but the service also has a bit of code that it gets to run in your client. Google Gears’s WorkerPool has a similar style, with message passing between threads that run in their own sandboxes. The big difference is that SandboxBridge messages are synchronous, and look like function calls and property gets.
SandboxBridges can be used at frame/iframe borders and at the LoaderInfo that is shared by parent and child when using Loader. Each side can expose APIs to the other. But it’s essential to resist the urge to bust the whole thing wide open. Don’t think it’s cute to just expose every AIR API to unprivileged content. Instead, expose high-level APIs that, when abused, will damage only your application, not the user’s whole system. A writeFile(file, data) API is just an invitation for attack. By contrast, savePreferences(data) will make your app less vulnerable to malicious content.

Importing

Importing.png
If you do trust the content, and want it to be able to share your runtime data, share your type definitions and execute in your space, you can import it into the application’s sandbox. This content will have full privilege. I can’t overstate this enough: content you import this way can completely take control of your application. And once it has control of your application, it can start doing evil things to the local machine. It can do anything your app can do.
So, why even bother with such a dangerous technique? Well, in some cases it really is what you want. If you’re trying to reduce the download size of your app, you can wait until you need some functionality before downloading it. Flex Modules, for instance, require that they be imported into the loader’s sandbox. The same technique works for localization data.
So to pull this content in safely, you need to prove that you can trust it. Given the ease of remote attacks (DNS hacks; man-in-the-middle attacks; owning up the “trusted” server; etc), imported modules must be signed, and the signature must be verified before loading the content.

  1. First, sign your modules. I’m pretty sure you can just use adt for this, then rip the signatures.xml out of META-INF. Then post the modules and signatures.xml

  2. When you download the modules, save them and signatures.xml somewhere in app-storage:/

  3. Use the XMLSignatureValidator class to validate that you downloaded modules that really were signed by you (or some known entity).

  4. Import the modules into your application sandbox. For SWF, use Loader.loadBytes(), pass a LoaderContext with allowLoadBytesCodeExecution=true. I doubt anyone will do *that* by accident.

In AIR 1.0, this is the best way to use Flex modules without taking dangerous risks. I expect there will be other techniques in future versions.
Import this way is not going to work for arbitrary third-party content. If you don’t know ahead of time who you trust to have signed it, you must not import it. Once again, we come back to The Spiderman Axiom: with great power comes great responsibility.
Blah. That’s way too long. But at least I have someplace to point people to when they ask!
Comments? Concise summaries?

20 Responses to Remote Plugins and Modules in AIR

  1. Dave Johnson says:

    Does this mean that you can import a SWF that can take control of your application and the developer just needs to be aware of the security implications, however, HTML AIR developers have to jump through hoops to import JavaScript “modules” that have similar security implications?

  2. Hey Ethan,
    Not being a Flex guru myself I was hoping you could explain the difference between the security models for flex/flash/swf and html/js based content loading…for dummies:)
    Thanks!

  3. @Dave Johnson – Both SWF and HTML/JS can be imported in a way that can take over the application. In both cases, developers need to be aware of the security implications, for their own sakes. Also, in both cases, they must jump through extra hoops in AIR. Existing code that uses import will not work without modification.
    @Andre Charland – At the most basic level, there is no difference in the security models for SWF and HTML/JS. Files loaded from the application directory using app:/ URIs are application files with full privilege. Other content files are loaded in browser-style sandboxes, based on their network domain. Import and privilege escalation are strongly restricted, and SandboxBridge communication is supported across sandboxes.

  4. David C. Moody says:

    This is a very interesting topic. Your site came up in our discussion on apollocoders forum: http://tech.groups.yahoo.com/group/apollocoders/message/2699
    As you can see, we’re having a discussion about how to accomplish enterprise applications using AIR. So that the users do not have to redownload the entire application each time there is a minor update. Loading modules at runtime via the network is a way to accomplish this but noone seems to know how to pull it off.
    Meaning, how do you develop the software. When you export relase builds it packages everything into one AIR file. So would I have to extract that and then just place the SWFs into the publicly accessible folder for the AIR app to dynamically load?
    It just seems that this would be easier? Maybe I’m overthinking.
    Thanks!
    -David

  5. @David Moody, you’re right that the normal AIR packaging via Flex Builder puts all modules in the AIR file. Ripping them out of the AIR file (it’s just a ZIP), along with the signatures.xml, is one way to approach it.
    Another way is to not use Flex modules, but instead use separate Flex applications. You might still want to explode the resulting AIR files. That’ll help with packaging, but will lead to the Flex framework being built into you “module” SWFs.
    I’m not enough of a Flex pro to offer much better suggestions than that, though I’m sure it’s possible to craft custom build tasks, or ant build.xml files…

  6. randy says:

    Hey Ethan,
    I tried your idea and hit a road block in using remote modules in AIR application.
    Step4 :Import the modules into your application sandbox Fails!! . I had a module built as SWF and used Loader.loadBytes(), passed a LoaderContext with allowLoadBytesCodeExecution=true , which failed. When i run it debug mode , I can see the system manager “unload the module swf “ before rendering the AIR application.
    As per the links(http://tech.groups.yahoo.com/group/apollocoders/message/2131)(http://tech.groups.yahoo.com/group/apollocoders/message/2124) , I guess in AIR 1.0 there is no way to load a module from any other directory other than the application directory . Also it should be noted that writing to the application directory is prohibited by the Runtime as per the link (http://bugs.adobe.com/jira/browse/FLEXDOCS-186) .
    Is there a way out ?

  7. @Randy,
    It turns out I’m missing some details about the magic mojo required to get the Flex ModuleManager to recognize modules loaded via Loader.loadBytes(). It’s not currently supported in Flex 3.
    I’ll track down the details and post them when I can. It’ll be a good excuse to post again!
    I think it’s something about hand-dispatching an event to the ModuleManager, or something…..

  8. jeremy mooer says:

    Ethan, have you heard anything or talked to anyone (while ‘onAir’) about this lately? Are there plans to open up some of these security doors for enterprise development?

  9. John says:

    Just raising another hand to say keen to see some way of updating modules and downloading new ones without re-downloading the entire app each time. I’m keen to use AIR for several elearning projects, modules fit the bill perfectly, but downloading whole app with modules could be 200+meg, which the client won’t stomach.
    Any more on the Loader.loadBytes() work around?

  10. jeremy mooer says:

    I found a work-around… it’s rather pathetic, but it works and will keep the company I work for moving forward on our migration to Flex (from a 20+ year old behemoth of an app). Being sort of an outsider to the Flex community, does anybody think I should be emailing members of the Flex SDK team about this? I mean, our app will have thousands of modules managing everything from world-wide shipping facilities to local labor and HR stuff. It seems as though Adobe has heavily invested in being a major player in the realm of business applications — hopefully it fulfills our needs and doesn’t turn into a waste of time because I’m really enjoying it.
    http://blog.smashedapples.com/2008/07/loading-remote.html

  11. John, for elearning uses, I’d imagined the Separate Sandboxes approach to be more appropriate. Each “bundle” could speak to the containing app through a defined SandboxBridge interface. What is it about your app that requires import?
    Jeremy, that’s a nice workaround. LocalConnection to the rescue! Again! Sure, it’s limited, but if it gets the job done, and you’re having a good time, we can all be happy.
    Everyone, sorry about the delay on more loadBytes() + modules details. So much work….

  12. Ethan, thanks for your reply, I’d be happy to work via separate sandboxes using a SandboxBridge interface – if there a way to create flex authored ‘bundles’ that could be loaded and initialised at runtime whilst being in a remote sandbox.
    Flex 3 Modules seems the only way to create flex authored ‘bundles’ that can be loaded and initialized at runtime. Flex 3 Modules only work in the local sandbox no? or I have missed something key? So the only way forward seems to be to find a method to import into the local sandbox. Or Adobe add a way to install & update individual flex 3 modules or Adobe allow flex 3 modules to run in a remote sandbox and operate via a bridge.

  13. Hai Nguyen says:

    Ethan, we faced the problem of loading module in AIR application, thanks for your hints and we already solve this issue. Here is the post and code http://blog.esofthead.com/?p=117.
    Have a nice day!

  14. Terry Corbet says:

    Well I’ve read this, and re-read this, and re-read this — each time thinking I am getting a little closer to understanding enough to actually succeed in ‘modularizing’ my AIR apps.
    So, without exposing just how little I know, may I ask for some restricted help. I have a version of Hai’s code running, but as posted, and as criticized elsewhere, at the time of his kind posting, he was ignoring signing. I guess I have to fix the signing problem. How do you “rip the signatures.xml out of META-INF” from a signed .air file?

  15. Terry Corbet says:

    Ok, so all of you with deep Adobe experience knew that an .air file was just yet another case in which they rename a .zip file. Ok, so I can extract the signatures.xml file with an appropriate archive tool.
    Has anyone ever looked at all those tags? Has anyone ever successfully peeled away the tags that seem inappropriate to the task at hand and managed to use XMLSignatureValidator to do something as seemingly simple as trace the value of signerCN to see if it actually is the same string that you used with the adt -certificate command?
    I guess that would be a minimal confirmation that something that came across the socket was probably what it was supposed to be. But is there more that you are saying needs to be done in order to successfully, safely use content downloaded from our web servers? Can you provide an example of an IURIDereferencer that would generally represent the sort of ByteArray data manipulation that you think we need to perform?
    Thank you. If you or anyone can find any place in the vast array of Adobe information sources where there is a succinct tutorial concerning the proper use of framework caching in the AIR environment, I am sure there are lots of us who would like to read it.

  16. Joe Ward says:

    @Terry,
    You would not want to change the signature file at all — doing so would invalidate the signature. In the IURIDereferencer, you need to return the element with the id, “#PackageContents”.
    Note that validating the signature file is only the first step. Now you have to examine the list of files in the #PackageContents elements. Notice the digest values (TsVvc…DI=/DigestValue>)?
    Those are digests of the files in the package. All you’ve accomplished so far is to make sure that no one has changed these values. To finish validation, you need to load each of the files into a byte array, compute your own SHA256 hash, and then compare that value to validated one from the signature file. If they match, then the file is unaltered.
    var file:File = sigFile.parent.parent.resolvePath(fileURLFromSignature);
    var stream:FileStream = new FileStream();
    stream.open(file, FileMode.READ);
    var fileData:ByteArray = new ByteArray();
    stream.readBytes( fileData, 0, stream.bytesAvailable );
    var digestHexString:String = SHA256.computeDigest( fileData );
    var digest:ByteArray = new ByteArray();
    for( var c:int = 0; c < digestHexString.length; c += 2 ){
    var byteChar:String = digestHexString.charAt(c) + digestHexString.charAt(c+1);
    digest.writeByte( parseInt( byteChar, 16 ));
    }
    digest.position = 0;
    var base64Encoder:Base64Encoder = new Base64Encoder();
    base64Encoder.insertNewLines = false;
    base64Encoder.encodeBytes( digest, 0, digest.bytesAvailable );
    var digestBase64:String = base64Encoder.toString();
    if( digestBase64 == digestFromSignature )
    {
    result = result && true;
    message += ” ” + reference.@URI + ” verified.\n”;
    }
    And for good measure, here’s an IURIDereferencer for AIR signatures:
    package
    {
    import flash.events.ErrorEvent;
    import flash.events.EventDispatcher;
    import flash.security.IURIDereferencer;
    import flash.utils.ByteArray;
    import flash.utils.IDataInput;
    /**
    * Validates an AIR application signature, taking advantage of the fact that the SignedInfo
    * element always refers to an element with id=”PackageContents”.
    */
    public class AIRSignatureDereferencer extends EventDispatcher implements IURIDereferencer {
    private const XML_SIG_NS:Namespace = new Namespace( “http://www.w3.org/2000/09/xmldsig#” );
    private var airSignature:XML;
    public function AIRSignatureDereferencer( airSignature:XML ) {
    this.airSignature = airSignature;
    }
    public function dereference( uri:String ):IDataInput {
    var data:ByteArray = null;
    try
    {
    if( uri != “#PackageContents” )
    {
    throw( new Error(“Unsupported signature type.”) );
    }
    var manifest:XMLList = airSignature.XML_SIG_NS::Object.XML_SIG_NS::Manifest;
    data = new ByteArray();
    data.writeUTFBytes( manifest.toXMLString());
    data.position = 0;
    }
    catch (e:Error)
    {
    var error:ErrorEvent = new ErrorEvent(“Reference error ” + uri + ” “, false, false, e.message);
    this.dispatchEvent(error);
    data = null;
    throw new Error(“Reference not resolvable: ” + uri + “, ” + e.message);
    }
    finally
    {
    return data;
    }
    }
    }
    }

  17. Terry Corbet says:

    Joe, many thanks for the ‘full Monty’ answer. Sorry to be slow in saying thanks, but I have nothing set up for notification of activity a this site, and just now came back to review.
    I have not worked on the signing problem, but will with the aid of your code/advice.
    I have worked on making sure that my approach to managing a ‘large’ application via a manifest of updatable modules is working, and it seems to be. However, in the closely-related matter of being able to take advantage of the Flex framework cache, the move from the 1.1 runtime to the 1.5 runtime seems to have killed that capability. In other words, I have been forced to compile each of my ‘mini-modules’ with static links to the framework instead of the vastly more efficient compilation with dynamic links.

  18. Joe Ward says:

    FWIW, I’ve written up an article on signing and validating in AIR:
    For Flex:
    http://www.adobe.com/devnet/air/flex/quickstart/xml_signatures.html
    For Flash CS:
    http://www.adobe.com/devnet/air/flash/quickstart/xml_signatures.html

  19. Many thanks to Hai and Joe for the articles and code. I now have signed modules downloading into app-storage, verifying them and importing them into the Application sandbox. I’d of had no chance without your help.

  20. RobMcM says:

    This is all very interesting guys. I have got various examples working but think I would prefer loading the modules into the sandbox and then providing an API for communication between the app and module.
    So if I have the module downloaded to the app storage, how do I then load it? Using the normal methods I get a security error (as expected), how do I tell it to use the sandbox?
    Thanks in advance