通过插件来扩展AIR应用程序[译]

转自:Adobe AIR Team Blog

翻译:小浣熊


已经有好几个客户和合作伙伴问起我们是否有可能为AIR应用程序编写一个安全的插件架构。虽然我们一直假定这可以办到,但我最终还是决定实际尝试一下来确认这一点。结论是:不仅可以为AIR应用程序编写安全的插件架构,而且实际上还非常简单。

 本文的以下部分提供了编写你自己的安全的AIR插件架构的所有信息。也提供了示例代码用作参考,并讨论了插件架构的作者需要认识的一些问题。

什么是插件?

首要问题:我所说的术语“应用程序插件”到底是什么意思?插件是一种供第三方来扩展应用程序功能的方式。两个很好的例子是Firefox add-onsGoogle Chrome extensions。这两个浏览器都具备使第三方来扩展应用程序功能的架构,并通过插件模型来提升用户体验。想让你的浏览器帮你拦截广告,通知你的新到邮件,或者在Wikipedia上查找东西吗?只需找到合适的插件安装就行了。插件可以让最终用户在标准的软件上按照他或她特定的需求和口味来进行自定义。

插件的挑战

那么为何AIR不打算支持插件呢?我们显然知道AIR可以支持某种形式的可扩展性(装载SWF,将它们加到显示列表中并调用其函数),但我们不是很有把握的是整个过程能有多安全。换句话说,能下载内容并在你的应用程序中运行是不够的,它还必须要能做到以一种高度安全和负责的方式进行。

除了安全问题,我们对于构建插件模型的最佳方法也根本没有一个很好的想法。虽然我们的目标不是创建一个官方支持的插件框架(所提供的代码仅是示例代码),但我们确实想要至少一个确凿的概念证明来使开发者有一个良好的起步。 

介绍“可插拔的SearchCentral”

去年我写了一个叫做SearchCentral的应用程序来验证AIR 2中新的NativeProcess API。它用一个叫做mdfind的命令来使用Spotlight(当然仅在Mac上)搜索你本地的硬盘,还能让你在同一个应用程序中用Google和Wikipedia来搜索东西。SearchCentral似乎是一个完美的候选对象。与其仅仅搜索三个“硬编码”的源,我决定创建SearchCentral的新版本“可插拔的SearchCentral”,或者就叫PSC,其支持安装搜索插件。每个插件添加搜索不同源的能力,并且以任何它想要的方式来展示搜索结果。

搜索插件可以从本地硬盘安装或者通过URL从网络安装。所有插件必须被签名,并且在安装之前要被验证。此外,最终用户要被展示一个插件安装对话框,其包含了他们作出是否安装的决定所需的所有信息。当插件安装后,PSC会变得更加个性化、功能多样而强大。(注意PSC意在一个示例应用和概念证明;虽然它很完整并且相当有用,但我还是有意去掉了大量功能来使代码尽可能简单。)

架构预览

PSC概念证明有两个不同的组件:可插拔的SearchCentral本身,以及搜索插件。下面是它们的简要描述。

可插拔的SearchCentral

PSC自身并不包含任何所有插件。相反,PSC提供了用来搜索的用户界面,装载和安装新插件的能力。它就是这样,而应用程序功能的剩余部分留给最终用户所安装的插件。

PSC插件

PSC插件是用ADT打包和签名的zip文件,其包含以下内容:

  1. 包含公有函数search的任何Flex应用。插件可以搜索任何资源(本地或远程的),并能以它想要的任何方式展示搜索结果。
  2. plugin.xml文件,其包含了该插件的信息,诸如它的名字、ID、描述,路径和版本等。
    由于PSC插件在应用程序沙箱中执行代码,并且它们可能通过互联网下载,因此它们必须被签名和验证,并在安装之前得到最终用户的认可。后面名为“安装和确认插件”的小节会详细讨论这个过程。

PSC的工作机制

下面是关于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从网络安装。当一个插件被定位或下载后,会经历如下过程:

  1. 插件被解压到一个临时目录。一旦插件描述文件从zip归档中解压出来后,插件ID会与已经存在的插件ID进行检查,确保同一个插件不会被安装两次。
  2. 从插件的META-INF目录中装载插件的signature.xml文件(关于AIR文件格式的更多信息——也是PSC插件所用的格式——可以阅读Oliver Goldman的文章,AIR文件格式笔记
  3. 验证该XML签名文件以确保它不包含任何不期望的数据或信息。如果它有,那么整个插件会立即被删除。
  4. 通过XMLSignatureValidator类来验证signature.xml文件中的签名以确保其有效性。
  5. 计算插件中所有文件的摘要并和signature.xml文件中宣称的摘要相比来进行验证。这个过程确保了插件在被签名后其中的文件没有被替换。如果任何一个摘要不匹配,那么该插件被被立即删除。
  6. 将展开的插件中的文件数目与插件的清单文件中所列的文件数目相比。如果插件包含任何不在清单中的文件(意味着它们不会被验证),那么整个插件就会被立即删除。
  7. 给最终用户展示包含在plugin.xml文件和插件签名中的数据,并询问他或她是否想安装这个插件。这些数据包括插件的名字、描述、发布者的姓名,以及签名是否已验证(意思是该插件所签署的证书是否能链接到已经安装在最终用户机器上的受信数字证书)等信息。
  8. 如果用户选择不安装该插件,插件会被立即删除而不会被加载。如果用户选择安装插件,它会被移到插件目录,然后被加载以便使用。

卸载插件

卸载插件很简单,删除该插件的目录,然后将其数据对象从插件选项框的数据提供者对象中删除。在插件被删除后,它可以在任何时候被再次安装。

创建插件

PSC插件可以是任何的Flex应用,只要它包含一个接受单个String参数(被搜索的项)的公有函数search。它可以进行任何它想要的搜索形式,并以任何方式展示其搜索结果。

对于现在这个特别的例子来说插件是很容易编写的,因为它们可以作为独立的Flex应用来编写和测试。它们甚至还可以创建为AIR程序,只要在最终的SWF被编译之前将根标签WindowedApplication改成Application标签就行。
下面描述的是创建和签署PSC插件的过程:

  1. 创建一个新的Flex项目。它可以是一个web或桌面项目。如果你的插件需要访问AIR API,确保使用桌面项目。
  2. 创建接受单个String参数(被搜索的项)的公有函数search。
  3. 进行你的搜索,并展示任何你想要的搜索结果。
  4. 在插件完成后,编译最终的SWF文件。如果你的插件是一个桌面Flex项目,确保将根标签从WindowedApplication改成Application。
  5. 为你的插件创建一个新的目录,然后将插件的SWF文件复制到该目录。(该目录将被压缩和签名。)
  6. 为你的插件创建一个描述文件并放到插件目录中(作为例子,可以查看插件示例代码中的plugin.xml文件)。由于我们打算用ADT来打包和签署插件,描述文件必须是一个合法的AIR应用描述文件,这意味着它需要包含如下元素:
    • id:插件的唯一ID。这用于确保同一个插件不会安装多次。
    • name:插件的名字,会在插件安装前显示给最终用户,并在安装后显示在插件选择框里。
    • description:插件的简短描述,会在插件安装前显示给最终用户。
    • filename: PSC没有使用,但ADT是要求的。(它可以用作插件目录的名字,但我的示例实现使用了全局唯一的ID,这样可以避免潜在的冲突)。
    • version:插件的版本,PSC当前没有使用。但在一个更完整的实现中,这可用于允许插件升级(以及用于防止意外降级,这通常被认为是安全风险)。
  7. initialWindow.content: 插件的SWF文件路径。用ADT打包和签署你的插件。ADT命令看起来应该是这样的:
    adt -package -storetype pkcs12 -keystore /path/to/code_signing_certificate my_plugin.zip my_plugin_dir/plugin.xml -C my_plugin_dir

现在你的插件已可以被安全地载入到PSC中了!

关键的工具和API

以下是使在AIR中编写一个安全的插件架构成为可能的工具和API的预览。

XMLSignatureValidator

XMLSignatureValidator真的是确保插件架构安全的关键。API文档中这样写道:

XMLSignatureValidator类验证XML签名文件是否格式正确且未修改,以及它是否使用链接到受信任数字证书的密钥进行签名(可选)。

如果没有XMLSignatureValidator类,那么AIR应用程序将很难决定插件是否用链接到已安装证书的证书进行签名的,也很难提取出关于插件发布者信息以便在插件安装前展示给最终用户。然而只需要行数的代码你的应用程序就能验证插件的完整性和真实性,和AIR运行时自身有一样的可靠性。

注意XMLSignatureValidator并不验证插件包中其它文件的完整性。这是插件架构自身需要完成的事(作为例子,可以查看PSC的源码)。XMLSignatureValidator能够验证签名文件自身没有被篡改,并提供了打包文件中所有文件的摘要,但插件架构必须将这些摘要与文件自身计算出的摘要做对比以确定插件没有被篡改。这一步一定不能省略。

关于XMLSignatureValidator类的更多信息和XML签名的大体介绍,可以查看Joe Ward的文章,创建和验证XML签名

UCFSignatureValidator

UCFSignatureValidator是一个ActionScript类,最初由Oliver Goldman编写,我将其引入到这个示例中。它负责验证signature.xml文件的结构、签名文件自身(使用XMLSignatureValidator类)以及包中的文件的摘要,还确保没有额外的文件被放入。虽然UCFSignatureValidator应该被视为示例代码,但它为安全的插件架构提供了一个健壮而全面的出发点。

ADT

ADT是到目前为止打包和签署插件的最容易的方式,因为它是AIR SDK的一个组成部分,并且许多AIR开发者已经有它的使用经验。你可以用ADT来签署你的插件,就好像它是一个AIR应用,并假设了你的插件描述文件包含所有ADT要求AIR应用的所有标签(上面所列的标签只是最低要求)。我发现AIR应用描述文件的格式对于我的插件架构来说是非常完美的,但如果你的架构需要关于插件的额外的元数据,你可能需要创建第二个描述文件格式。或者你可以用其它方法来签署和打包你的插件,但一定要仔细读一下XMLSignatureValidator的文档以确保你的方法会产生兼容的签名。

FZip

FZip是一个ActionScript项目,我的示例插件架构用它来解压插件。它开箱即用,可以非常完美地解压用ADT打包的插件。

as3crypto 

我用了as3crypto项目,因为它提供了SHA1和SHA256哈希算法实现,可以用于计算插件包中的文件的摘要。我本可以用Flex的mx.utils.SHA256类,因为ADT当前只是用了SHA256,但由于对于signature.xml文件来说用SHA1算法也是可能的,于是我决定使用as3crypto,以便万一使用除ADT之外的其它方法来签署插件。

需要注意的问题

虽然为AIR应用程序编写一个健壮的插件架构相同容易,还是有一些事情需要开发者和最终用户注意:

  • 性能。由于解压和验证插件是在主应用程序线程中完成的,如果你的插件太大,它们可能导致应用程序在验证过程中出现不响应的状况。对于PSC我从未遇到性能问题,但我能想象一旦插件达到一定尺寸,用户体验会下降。如果你遇到了这种情况,一定要最终用户提供足够的反馈,这样他们能知道正在发生什么。在极端的情况下,你可能需要考虑创建一个“循环帧”并在不同的工作单元中进行验证工作,这样界面可以在期间被更新。
  • 用ADT进行签名。正如之前讨论的,打包和签署插件的最容易方式是用ADT,但由于ADT的主要工作时签署和打包AIR应用,你不得不遵循一些AIR应用程序的惯例(像使用标准的AIR应用程序描述文件)以便正常工作。幸运的是,我发现该应用程序描述文件格式对于插件来说非常完美,因此我使用ADT没有任何问题,但这一点是设计一个插件架构时需要注意的。(注意虽然用ADT来签署应用程序插件目前没有明确表示支持,但我已经验证它可以很好地工作。)
  • 安装后的安全性。在这里描述的并在PSC示例应用中实现的插件架构能够在安装时验证插件发布者的身份,并能验证插件包中所有文件的完整性,但它不能确保在安装后插件的完整性,理解这一点是很重要的。换句话说,本插件框架的要点是在安装的时候验证插件,以使你免受来自插件在服务器上被替换了或者在某种程序上被改变的攻击,但它并不保证插件在安装后的完整性。所以在插件被验证和安装后进行恶意的篡改过程是可能的。由于在插件每次被加载时重新验证是不切实际的,因此目前还没有办法免受此类攻击。也就是说,如果你已经在无意中在你的机子上安装了有篡改应用程序插件能力的恶意软件,通常可以认为你的机子已经收到了影响,而更新应用程序插件可能是你最不用担心的。

额外的资源 

你可能感兴趣的:(xml,应用服务器,网络应用,Flex,AIR)