Posts in Category "AIR"

EncryptedLocalStore and Storage Reliability

Recently I’ve fielded yet another question about the reliability of the EncryptedLocalStore API, which is usually a good indication that a topic deserves a post.

They key thing to understand about all storage options, encrypted or otherwise, is that there are no guarantees. Hardware failures, software failures, user error, or even deliberate user action can all lead to data disappearance and data corruption.

Applications should always be prepared to deal with these scenarios. This doesn’t necessarily mean you need fancy data-recovery options, although it might. It does mean your application shouldn’t be rendered useless upon encountering, for example, a malformed configuration file.

Does EncryptedLocalStore provide reliable storage? Probably not any more so than storing data anywhere else on a given device, as all of the same failure modes apply. Probably a bit less so, as the complex nature of the encryption mechanisms provide that many more things that can go wrong.

Note that this doesn’t excuse the presence of defects in those mechanisms. Rather, the point here is that more complex mechanisms have more points of failure, and sooner or later something will go wrong. If you’re concerned with reliability, your only realistic option is to plan for that scenario.

If you use EncryptedLocalStore to cache confidential information, such as passwords, then this issue is probably not serious, since users can generally re-enter that information. In effect, the user servers as the backup for this data.

If, however, you use EncryptedLocalStore to maintain encryption keys, or other items that the user is not aware of or cannot reproduce, then you should explore providing other backup mechanisms. If the encrypted data is valuable, you should probably back up the data and the key. If the data is a cache and is encrypted only locally, then logic to abandon and re-fill the cache may be sufficient. Whatever your scenario, an appropriate plan can no doubt be devised.

Deployment Configuration and AIR Applications

All but the simplest of applications accept, or even require, configuration data that affects their behavior. These settings might affect the appearance of a UI element, an algorithm used to compute a calculation, or where to locate a service, such as a database, required by the application.

AIR developers are often tempted to embed this configuration data into the application package. This appears to be a convenient option because standard methods for deploying AIR applications, whether on the desktop or on mobile devices, don’t provide any alternative way of packaging this data with the application. On the other hand, it is inconvenient if more than one configuration is required, because then a separate package must be created, signed, and managed for each set of configuration options.

I encourage you to avoid packaging configuration options with your application. In addition to creating a package management problem that is otherwise avoided, it makes it impossible to move a copy of the application between different environments, be they test, staging, production, or something else. (Jez Humble and David Farley do an excellent job of covering this issue in their book, Continuous Delivery.)

Instead, I encourage you to deploy configuration data via another mechanism. The simplest approach is to use a file deployed to a well-known location. This approach works well in the enterprise, where it has a well-established history (think /etc on Unix), makes the data easily accessible to AIR applications, and is support by most deployment tools. If you’re in a closed environment your platform of choice might offer other options; Windows, for example, offers Active Directory.

It can be useful, by the way, to have a built-in default set of values, especially in the consumer space. This approach makes it easier for your application to be tested against a mocked up service provider in house and then, when deployed, automatically target the real service. For example, an application that talks to Facebook might use your mocked Facebook service in-house, but when deployed by customers always connect directly to Facebook.

As implied above, Adobe does not provide tools that directly support the deployment of configuration data. The AIR team elected not to enter this space because (a) it’s populated by existing players, such as IBM and Microsoft, with deep expertise in this area, and (b) a significant number of customers we speak with already have an existing solution in place. Our approach, then, has been to integrate with existing deployment tools and their capabilities. For more on this, see Distributing AIR in the Enterprise, by Peter Albert and Michael Labriola.

Approaches to Modular AIR Applications

As applications grow in size and complexity, it becomes necessary to break them up into manageable pieces. To some degree this can be handled at development time by maintaining a carefully-structured source code base.

Compiling the application into a single, monolithic deliverable may itself become a roadblock. For example, it may be desirable instead to compile and validate these pieces separately, and then assemble the resulting binaries. The pieces may be developed and shipped on different schedules, and thus different portions of the application updated independently. Or, the pieces may even be developed by other parties, as is often the case for applications supporting plugins. Regardless, it often becomes necessary to defer loading some of these pieces until runtime.

AIR currently supports two basic approaches for this kind of runtime assembly: Loading code into a network sandbox, and loading code into the application sandbox. The two approaches have different capabilities and different limitations. When designing large applications, it’s good to be aware of these trade-offs.

Network Sandbox

Code is loaded into a network sandbox via the Loader.load() API by specifying the URL of the target code. Code in this sandbox is restricted from accessing AIR-specific APIs, but has access to the same set of APIs that content running in Flash Player can access. The network sandbox essentially is Flash Player.

Interestingly, by restricting the API to the Flash Player API, the loaded content can necessarily be run in Flash Player or AIR. This may be especially useful if your application runs sometimes in the browser and sometimes in an application.

The API restrictions also mean that you don’t have to trust the code you load. The network sandbox prevent access to potentially dangerous APIs, such as the filesystem API. You can selectively open up access via the sandbox bridge capability.

Because the runtime knows where the code was loaded from, the code can use relative addressing to access other network resources, including additional network-hosted code. This allows for such code to easily be moved between different web servers, including between test and production environments.

The network sandbox is typically not suitable when the code your loading is supposed to function as an integral part of the application, with full access to other parts of the application and to the AIR APIs. For that, you want the application sandbox.

Application Sandbox

Code is loaded into the application sandbox via the Loader.loadBytes() API. Code in this sandbox runs as if it were installed along with your application, having full access to all of the AIR APIs. This facility provides the basic underpinnings for a complete plugin model. You can use that model to modularize your own application, or even open it up to third-party components.

Code loaded via this method is granted full API access, so it is essential that the application validates that it trusts the code before loading it. This can be accomplished in a secure fashion by using code signing, and validating the signature before loading the code. It may also be sufficient to obtain the code via a secure URL, but it should be noted that this protects the code only while in transit, and not after it is stored locally. To encourage secure use of this API, it loads the code, as the method names suggests, directly from a ByteArray and not from a URL.

Code loaded via this API does not retain its origin (URL), and therefore cannot access other code using relative URLs. Again, this was done to encourage secure use of the API. If this code can implicitly load additional code off the network, it is not sufficient to validate just the first SWF; each link of that chain requires careful validation.

Note that, because the origin of the code is removed, you can’t combine granting access to the application sandbox with the deployment ease of loading a SWF and associated RSLs from the network. If one module depends on another, some other referencing mechanism must be used.

Futures

It’s easy to imagine extending these models in a variety of ways. For example, while it is currently possible to create a plugin mechanism for an AIR application, the validation work has to be done by the application. This in turn prevents it from integrating with other runtime features, like RSLs. By expanding runtime support, we could potentially allow these features to work together.

We are actively exploring how AIR applications are using these techniques today, and how we might enhance our support for building large applications in the future. If you have feedback in this area, please let us know via a comment or via ideas.adobe.com/air.

References

For more in this area, you might want to read:

Replacing NativeWindow on mobile devices and TVs

The AIR NativeWindow API is a staple of desktop AIR applications, used to open multiple windows on the screen, each with its own stage. On mobile devices and TVs, the class is unsupported. So how does an application go about opening a window?

It doesn’t. On mobile devices and TVs, AIR applications can render only to the single available stage. The stage is automatically create and made visible when the application launches, and remains available until the application exits. In other words, it behaves essentially the same as the Stage does for Flash Player content.

Most code need concern itself only with the stage, and in this way can be made more portable between desktop, mobile, and TV deployment. For example, the stage directly provides information about its size. It also retains the ability to enter full-screen mode, and on mobile devices provides information about screen orientation.

If you have code that needs to check at runtime whether or not it can create new windows, simply check NativeWindow.isSupported.

Replacing Updater on mobile devices and TVs

Desktop AIR developers are, hopefully, familiar with our advice to implement an auto-update mechanism for your application first, even before you add your first feature. But what should you do on mobile devices or TVs, where the Updater API isn’t available?

On mobile devices and TVs, you should be happy that the platform has already implemented an update mechanism for you. As part of the better-developed application delivery ecosystem on these environments, applications are downloaded from application marketplaces of one type or another. These same marketplaces provide an update mechanism for these applications. Details vary as to whether the updates are installed automatically or require user intervention but, in any case, these will be the update mechanism users on these devices will be expecting.

If you need to write code that works in both places, you can check support at runtime via Updater.isSupported. This is a decision you might want to take into account earlier on, however, since there’s no value in including additional code, like the AIR Updater Framework, in applications targeting mobile or TV.

Replacing EncryptedLocalStore on mobile devices and TVs

If you’re familiar with the AIR desktop APIs and now looking at AIR on mobile or TVs, you may have noticed that the EncryptedLocalStore API is not currently available in either of these new targets. Here some tips on handling the situation.

First, consider whether or not the information your storing needs the additional security provided by the EncryptedLocalStore API. This is typically appropriate for items like passwords that need to be protected even from other software on the same device. However, items like configuration settings are best stored in a SharedObject or directly in a file.

Second, consider whether or not it’s appropriate to save this information on the target device without these protections. Unlike the desktop, mobile devices and TVs typically isolate all of the data stored by each application. A password stored on such a device therefore isn’t exposed to other applications, but can still be exposed if the device itself is compromised. Whether or not this is sufficiently secure depends on your application.

If the data is sensitive, and too sensitive to store without encrypted, you’ll have to fallback to querying this data from the user each time it’s required.

Finally, if you need to write shared code that can handle either situation, you can do so by writing a wrapper class that handles the switch at runtime. To perform a runtime check for EncryptedLocalStore functionality, simply query EncryptedLocalStore.isSupported.

More On Sharing HTTP Cookies with AIR Applications

In an earlier post, I mentioned that AIR applications can share cookies with the system browser, and this in turn can be used to share single sign-on (SSO) information stored in these cookies with an AIR application. Unfortunately, this turns out to be even less useable than I realized.

As I mentioned in the original post, there are clear limits to this capability. For example, it won’t work with browsers like Firefox and Chrome, as they don’t share the system cookie database at all.

On Windows, there are two system cookie databases: the default used by applications using WinInet, and a special, second location used by Internet Explorer’s protected mode. By default, most web sites visited will be visited in protected mode. AIR applications, as regular WinInet clients, still use the default cookie database. The end result is that cookies are not shared between the two.

This can be mitigated in some circumstances by changing certain IE security settings. For more details, see this Microsoft knowledge base article. Fundamentally, however, this is not a reliable solution for achieving SSO.

If you’d be interested in a solution that permitted this kind of cookie sharing, for SSO or other purposes, please let me know via the comments or at ideas.adobe.com/air.

Certificate Support in AIR for Linux

In an earlier post I explained how to use TLS client authentication for AIR applications on Windows and Mac OS. Commenter Arlen asked how to do the same on Linux; unfortunately, TLS client authentication is not supported in AIR for Linux.

The first problem is that, unlike Windows and Mac OS, Linux doesn’t have a standardized, easily accessible certificate store available. Instead, AIR bundles its own certificate stores. (See this Adobe knowledge base article for information about managing those certificate stores.) Other Linux applications typically do the same. Even if client authentication was supported, it would have be configured separately for AIR applications versus other applications, thus making it much less useful than on Windows or Mac OS.

The second problem is that Linux doesn’t have a standardized, easily accessible HTTP stack that supports TLS client authentication—instead, applications have to bundle their own implementation. That, of course, doesn’t make it impossible for AIR to add this support, but it means it requires a non-trivial engineering investment.

To date, these two issues have kept us from adding TLS client authentication support on Linux. If you’d like to see it added, I encourage you to vote for it on the Adobe AIR Ideas site.

An Updater Framework for Native Application Installers

In an earlier post about native application installers, I mentioned that implementing an updating mechanism for applications that use this deployment option is currently an exercise for the user, as the updater framework included in the AIR SDK does not support native application installers.

Fortunately, Adobe Platform Evangelist Piotr Walczyszyn has written just such a framework. If you are adopting native application installers, it’s worth a look.

With respect to my earlier comments about securing these updates, it’s worth noting that secure use of Piotr’s framework currently requires hosting HTTPS downloads for the installers.

API Tip: File.nativePath ≠ File.url

Recently we’ve seen a number of developers get tripped up on code similar to this sample:

// Don’t copy; this code is wrong
myURLLoader.load( new URLRequest( myFile.nativePath ));

This code is wrong: myFile.nativePath returns a file path, but the constructor to URLRequest expects a URL. It’s easy to make this mistake for two reasons:

  1. In ActionScript, both file paths and URLs are of type String. Thus, the compiler cannot determine that one is being used in place of the other.
  2. Sometimes, especially on Windows, this code works anyway. This is a side-effect of some too-lenient URL parsing code in the runtime.

Taken together, these two issues make it easy to write incorrect code that works on Windows and yet fails rather inexplicably when run on Mac OS or Linux. Fortunately, the fix is easy: substitute myFile.uri for myFile.nativePath, and all will work as expected.

// Correct version of the code
myURLLoader.load( new URLRequest( myFile.url ));