Main

February 24, 2009

Using InvokeEvent.reason in AIR 1.5.1

AIR 1.5.1, released today, is not quite a typical dot release containing just bug fixes. It also contains one minor—albeit useful—new feature.

Applications can already detect when they've been invoked by listening for the InvokeEvent that is dispatched by NativeApplication. This is dispatched when they're started from the command line, via the GUI, or by opening an associated file.

Applications can also register to be launched automatically during user login. However, prior to 1.5.1, there was no reliable way to distinguish this login case from the the other reasons an application might be started. That turns out to be awkward: In the login case, applications often want to avoid opening new windows. In all other cases, they generally want to make sure they do open a new window.

Starting with AIR 1.5.1, the InvokeEvent class contains a new property, "reason", that distinguishes between these two cases. It has two possible values: "standard" and "login". These are, I hope, self-explanatory.

Note that in order to use this new API, you must update your application to use the AIR 1.5.1 namespace by changing its descriptor. (There are no other changes to the descriptor schema in this release.) The new descriptor will trigger an auto-update to AIR 1.5.1 should someone attempt to install your application against an older version, thus guaranteeing that the new API will actually be available.

December 5, 2008

Changes to HTMLLoader.loadString() in AIR 1.5

For AIR applications bound to version 1.5 and later, the default behavior of the method HTMLLoader.loadString() has changed. This may impact your application; here's the how and why of the change. (For Flex users, the following discussion also applies to setting the HTML.htmlText property.)

AIR applications are desktop applications and, consistent with that fact, AIR does not prevent you from doing dangerous things like fetching remote content and running it locally. However, it's rare that such things are desirable and, when they are, they should be done explicitly and carefully. AIR APIs are therefore generally designed to make doing safe things easy and dangerous things hard.

Prior to the AIR 1.5 behavior, HTML content loaded via HTMLLoader.loadString() was placed in the application sandbox. This content has full access to the local machine. Whether or not this is reasonable depends on where you get the string that you load from and what that string contains. Since it's easy for that string to come from untrusted sources, this provides an easy path for injecting untrusted code into your application.

To address this, starting in AIR 1.5, loadString() defaults to loading content into the browser sandbox. This is the safe thing to do since untrusted code, when running in the browser sandbox, is unable to operate with the same permissions as are otherwise granted to your application.

If you want the old behavior, you can set HTMLLoader.placeLoadStringContentInApplicationSandbox = true. If you do this, remember that you are taking on responsibility for dynamically loading code into the application sandbox and all of the risks that entails. Ethan Malasky's blog has some great posts in this area. Still, I don't generally recommend it—it's hard to get right.

One final note: This change will not break your existing applications. The new behavior is bound to the namespace used in your application descriptor file. Applications using the 1.0 or 1.1 namespaces will operate as before. You will have to take this change into account when you update to the 1.5 (or later) namespace.

July 30, 2008

Cleaning Up Sensitive User Data

Applications sometimes store potentially sensitive data. Sometimes the items are obviously so, like store passwords. Sometimes it's less obvious but still sensitive, like your browser history.

I was recently asked how an AIR application could make sure that sensitive data it stores was cleaned up as part of the application uninstall process. That, unfortunately, isn't possible.

Not all is lost, however. Instead of depending on the uninstall process, applications that store sensitive data should include an explicit method (e.g., a menu item) for clearing that data. Your browser, for example, probably already has such an option. Users can use this before they uninstall to make sure that data is cleared out. As a bonus, they can use it without uninstall, too.

How to clean up the data? In general, deleting the files is sufficient. If you're using the EncryptedLocalStore API, use the reset() method to erase it.

June 18, 2008

AIR API Tip: Don't write to File.applicationDirectory

As a number of AIR developers have discovered, it's hard—but not impossible—to write files in the application's install directory. That's the same location given by File.applicationDirectory.

Granted, it's often a tempting thing to do. For example, many applications include a database or configuration file with some pre-filled data. Once installed, they often want to update that file, say with user-specific data or preferences.

The first problem with this is that it's not reliable. On some operating systems—Vista, for example—the installation directory is protected by the operating system. Even if AIR lets you write to this location Vista won't. So if you want to write portable applications, don't do this.

The second problem with this is that it's not safe. Any code written into this directory runs with application privilege, which can compromise your application, which can compromise the user's machine. That's why Vista protects these directories.

The third problem is that it invalidates your application's signature. Among other things, that means you won't have access to the encrypted local store any more.

It's also unnecessary. A safe and allowable alternative is to write somewhere into the user's directory. If you still want a starter file, it's easy to copy one from your install directory to the per-user location. Note that this also avoid problems if there are multiple users on the same machine.

In order to help developers avoid this pattern, AIR will prevent write access to this directory in most cases, and even when the underlying operating system allows it. There are currently ways around this, as it's advisory—not mandatory. However, the ability to go around this restriction is often misinterpreted as a defect, and we may close this off in a future release. You've been warned.

April 7, 2008

Custom Update UI for Application Updates

I've just finished my talk on the Brussels stop of the On AIR tour. I was discussing the importance of the application update mechanism in the context of dealing with security issues and mentioned the <customUpdateUI> feature. According to the show of hands in the audience, no one knows about this feature, which is a real shame.

The feature itself is simple. By default, if your application is already installed and an .air file for your application is opened, AIR will handle the situation. It will display appropriate UI; typically, you'll see the old and new version numbers and be asked if you wish to upgrade.

That's less than ideal for two reasons. First, most applications have their own update mechanisms, update UI, and so on. This default behavior circumvents all of that.

Second, it puts the decision about which version of the application is newer in the user's hands. Assuming you use a straightforward versioning scheme, this is typically not an issue. However, if an attacker can convince a user to install an older version of the application, then they may be able to use a security vulnerability you've already patched to compromise the user's machine.

To avoid these drawbacks, simply declare the <customUpdateUI> element in your descriptor. With that set, any activation of an .air file for your application will cause (a) your application to be started, if it's not already running and (b) the path to the .air file to be delivered to your application via an InvokeEvent. Your application simply needs to capture that event and handle it. You can likely handle it by re-using the logic for your own auto-update mechanism.

That's all there is to it. Full documentation is also available.. Give it a try; I hope to see more hands go up next time I ask who's seen this.

April 4, 2008

The AIR Browser API and User Events

The AIR browser API lets web applications detect, install, and launch AIR applications. There are some restrictions on its use, however:


  • Applications have to opt-in to the detect and launch capability. This is done by specifying <allowsBrowserInvocation> in the application's descriptor.
  • Installing and launching applications can only be performed in the context of a user event

"In the context of a user event" means that the method is called during an event handler for some event, such as a mouse click, that's initiated by a user action, such as clicking on a button. Note that this doesn't mean all mouse events qualify, as they can also be dispatched programmatically.

Why this restriction? To protect the user against malicious (or just really annoying) web pages. WIthout this restriction, pages could install and launch applications as soon as the page is loaded, over and over again, until you give up in disgust and quit using the Internet.

This restriction occasionally trips people up when using the browser API because it's tempting to wait for a user event, then call getApplicationVersion() to determine whether or not the application is installed, and finally call installApplication() or launchApplication(). But this won't work because the getApplicationVersion() call is asynchronous; when the specified callback function executes, the code is no longer part of the original user event.

Note that getApplicationVersion(), however, need not be called during a user event. The trick, then, is to call this method right away, when your web page loads. By the time the user clicks on the button, you'll already know whether you need to install or launch and can do either immediately within the context of the user event.

One final note: consider calling getApplicationVersion() periodically from a Timer. That way, if the application install state does change while the page is open, subsequent clicks on that button will continue to do the right thing.

February 19, 2008

AIR API Tip: Use HTTP_RESPONSE_STATUS, not HTTP_STATUS

AIR extends the existing Flash URLLoader, URLStream, and FileReference classes with a new event type, HTTP_RESPONSE_STATUS. If you're writing an AIR application, you probably want to use this new event instead of HTTP_STATUS. Both events are instances of HTTPStatusEvent, but there are two major differences.

Additional Properties The HTTPStatusEvent has two new properties, responseHeaders and responseURL. These properties are available for HTTP_RESPONSE_STATUS but not HTTP_STATUS.

Earlier Dispatch The HTTP_STATUS event is dispatched after the request is completed and immediately before the COMPLETE event itself is sent. HTTP_RESPONSE_STATUS is dispatched as soon as the headers have been processed, before the data is available. If your request contains a lot of data or is made over a slow link, this can be significantly earlier.

Why did AIR introduce the new event type, instead of just re-using HTTP_STATUS? Compatibility: We didn't want to break any existing code.

November 29, 2007

Version Numbers and the Updater API

AIR applications can update themselves via the Updater API. It's straightforward to use: you just provide an AIR file containing the new version of your application and the version number you're upgrading to. Downloading the AIR file and displaying any appropriate UI is left to the application.

When we designed this API, I thought we'd get a lot of questions about why the version number had to be provided. After all, the version was itself embedded in the AIR file. Surely we could just grab the version number from there, right?

Disappointingly, few have asked.

Disappointing because that argument exists to help you protect your application from downgrade attacks. Downgrade attacks work like this: some earlier version of your application has a vulnerability, but has since been patched. The attacker tricks your update mechanism into using the Update API to install that old version, thus re-introducing the vulnerability. Then they attack the vulnerability.

To protect against this attack, the application must validate before it begins the update that the version it's updating to is, in fact, a newer version. During the update process itself, AIR will validate that the version number you've validated and passed to the update call does in fact match the version in the AIR file. If they don't match exactly, the update will fail.

Why doesn't AIR check that the version is newer? Because we didn't want to impose a version numbering scheme on anyone.