翻译:小浣熊
已经有好几个客户和合作伙伴问起我们是否有可能为AIR应用程序编写一个安全的插件架构。虽然我们一直假定这可以办到,但我最终还是决定实际尝试一下来确认这一点。结论是:不仅可以为AIR应用程序编写安全的插件架构,而且实际上还非常简单。
本文的以下部分提供了编写你自己的安全的AIR插件架构的所有信息。也提供了示例代码用作参考,并讨论了插件架构的作者需要认识的一些问题。
首要问题:我所说的术语“应用程序插件”到底是什么意思?插件是一种供第三方来扩展应用程序功能的方式。两个很好的例子是Firefox add-ons和Google Chrome extensions。这两个浏览器都具备使第三方来扩展应用程序功能的架构,并通过插件模型来提升用户体验。想让你的浏览器帮你拦截广告,通知你的新到邮件,或者在Wikipedia上查找东西吗?只需找到合适的插件安装就行了。插件可以让最终用户在标准的软件上按照他或她特定的需求和口味来进行自定义。
那么为何AIR不打算支持插件呢?我们显然知道AIR可以支持某种形式的可扩展性(装载SWF,将它们加到显示列表中并调用其函数),但我们不是很有把握的是整个过程能有多安全。换句话说,能下载内容并在你的应用程序中运行是不够的,它还必须要能做到以一种高度安全和负责的方式进行。
除了安全问题,我们对于构建插件模型的最佳方法也根本没有一个很好的想法。虽然我们的目标不是创建一个官方支持的插件框架(所提供的代码仅是示例代码),但我们确实想要至少一个确凿的概念证明来使开发者有一个良好的起步。
去年我写了一个叫做SearchCentral的应用程序来验证AIR 2中新的NativeProcess API。它用一个叫做mdfind的命令来使用Spotlight(当然仅在Mac上)搜索你本地的硬盘,还能让你在同一个应用程序中用Google和Wikipedia来搜索东西。SearchCentral似乎是一个完美的候选对象。与其仅仅搜索三个“硬编码”的源,我决定创建SearchCentral的新版本“可插拔的SearchCentral”,或者就叫PSC,其支持安装搜索插件。每个插件添加搜索不同源的能力,并且以任何它想要的方式来展示搜索结果。
搜索插件可以从本地硬盘安装或者通过URL从网络安装。所有插件必须被签名,并且在安装之前要被验证。此外,最终用户要被展示一个插件安装对话框,其包含了他们作出是否安装的决定所需的所有信息。当插件安装后,PSC会变得更加个性化、功能多样而强大。(注意PSC意在一个示例应用和概念证明;虽然它很完整并且相当有用,但我还是有意去掉了大量功能来使代码尽可能简单。)
PSC概念证明有两个不同的组件:可插拔的SearchCentral本身,以及搜索插件。下面是它们的简要描述。
PSC自身并不包含任何所有插件。相反,PSC提供了用来搜索的用户界面,装载和安装新插件的能力。它就是这样,而应用程序功能的剩余部分留给最终用户所安装的插件。
PSC插件是用ADT打包和签名的zip文件,其包含以下内容:
下面是关于PSC和插件架构工作机制的更详细的描述。
当PSC启动时,它在应用程序存储目录(File.applicationStorageDirectory)中查找一个叫做“plugins”的目录。如果目录不存在(例如,如果这是应用程序第一次运行,或者如果plugins目录被最终用户删除了),初始化代码会创建该丢失的目录。PSC然后迭代plugins目录下的所有目录并加载它发现的所有插件的元数据。只有插件元数据被加载到内存(名字、ID、路径、描述和版本)而不是插件本身,这是为了使内存消耗下降。
当用户选择一个插件,PSC用插件的路径元数据以及AIR的文件系统API来把插件的SWF文件装载入ByteArray中。之后这些字节数据通过SWFLoader加入到显示列表中。在应用程序的初始化过程中,一个LoaderContext被设置给SWFLoader,并且它的allowLoadBytesCodeExecution属性被置为true以允许插件执行ActionScript字节码。
当用户输入一个搜索项并按回车键或者点击“Search”按钮时,PSC调用插件的公有函数search并传入搜索项。如果插件没有成功装载,或者插件没有公有函数search,则一个错误信息会显示给用户让他们知道插件是坏掉的。
插件可以通过本地硬盘安装,也可以通过URL从网络安装。当一个插件被定位或下载后,会经历如下过程:
卸载插件很简单,删除该插件的目录,然后将其数据对象从插件选项框的数据提供者对象中删除。在插件被删除后,它可以在任何时候被再次安装。
PSC插件可以是任何的Flex应用,只要它包含一个接受单个String参数(被搜索的项)的公有函数search。它可以进行任何它想要的搜索形式,并以任何方式展示其搜索结果。
对于现在这个特别的例子来说插件是很容易编写的,因为它们可以作为独立的Flex应用来编写和测试。它们甚至还可以创建为AIR程序,只要在最终的SWF被编译之前将根标签WindowedApplication改成Application标签就行。
下面描述的是创建和签署PSC插件的过程:
现在你的插件已可以被安全地载入到PSC中了!
以下是使在AIR中编写一个安全的插件架构成为可能的工具和API的预览。
XMLSignatureValidator真的是确保插件架构安全的关键。API文档中这样写道:
XMLSignatureValidator类验证XML签名文件是否格式正确且未修改,以及它是否使用链接到受信任数字证书的密钥进行签名(可选)。
如果没有XMLSignatureValidator类,那么AIR应用程序将很难决定插件是否用链接到已安装证书的证书进行签名的,也很难提取出关于插件发布者信息以便在插件安装前展示给最终用户。然而只需要行数的代码你的应用程序就能验证插件的完整性和真实性,和AIR运行时自身有一样的可靠性。
注意XMLSignatureValidator并不验证插件包中其它文件的完整性。这是插件架构自身需要完成的事(作为例子,可以查看PSC的源码)。XMLSignatureValidator能够验证签名文件自身没有被篡改,并提供了打包文件中所有文件的摘要,但插件架构必须将这些摘要与文件自身计算出的摘要做对比以确定插件没有被篡改。这一步一定不能省略。
关于XMLSignatureValidator类的更多信息和XML签名的大体介绍,可以查看Joe Ward的文章,创建和验证XML签名。
UCFSignatureValidator是一个ActionScript类,最初由Oliver Goldman编写,我将其引入到这个示例中。它负责验证signature.xml文件的结构、签名文件自身(使用XMLSignatureValidator类)以及包中的文件的摘要,还确保没有额外的文件被放入。虽然UCFSignatureValidator应该被视为示例代码,但它为安全的插件架构提供了一个健壮而全面的出发点。
ADT是到目前为止打包和签署插件的最容易的方式,因为它是AIR SDK的一个组成部分,并且许多AIR开发者已经有它的使用经验。你可以用ADT来签署你的插件,就好像它是一个AIR应用,并假设了你的插件描述文件包含所有ADT要求AIR应用的所有标签(上面所列的标签只是最低要求)。我发现AIR应用描述文件的格式对于我的插件架构来说是非常完美的,但如果你的架构需要关于插件的额外的元数据,你可能需要创建第二个描述文件格式。或者你可以用其它方法来签署和打包你的插件,但一定要仔细读一下XMLSignatureValidator的文档以确保你的方法会产生兼容的签名。
FZip是一个ActionScript项目,我的示例插件架构用它来解压插件。它开箱即用,可以非常完美地解压用ADT打包的插件。
我用了as3crypto项目,因为它提供了SHA1和SHA256哈希算法实现,可以用于计算插件包中的文件的摘要。我本可以用Flex的mx.utils.SHA256类,因为ADT当前只是用了SHA256,但由于对于signature.xml文件来说用SHA1算法也是可能的,于是我决定使用as3crypto,以便万一使用除ADT之外的其它方法来签署插件。
虽然为AIR应用程序编写一个健壮的插件架构相同容易,还是有一些事情需要开发者和最终用户注意: