Adobe AIR 中的 HTML 安全性

Adobe AIR 1.0 和更高版本

本主题介绍 AIR HTML 安全体系结构,以及如何使用 iframe、帧与沙箱桥设置基于 HTML 的应用程序和安全地将 HTML 内容集成到基于 SWF 的应用程序。

运行时会强制执行规则,并提供克服 HTML 和 JavaScript 中的潜在安全漏洞的机制。不论您的应用程序为主要采用 JavaScript 编写,还是您将 HTML 和 JavaScript 内容加载到基于 SWF 的应用程序,强制执行的规则都相同。应用程序沙箱和非应用程序安全沙箱中的内容具有不同的权限。将内容加载到 iframe 或 frame 中时,运行时会提供一种安全的 沙箱桥 机制,该机制允许 frame 或 iframe 中的内容能够与应用程序安全沙箱中的内容进行安全通信。

AIR SDK 为呈现 HTML 内容提供三个类。

HTMLLoader 类提供 JavaScript 代码和 AIR API 之间的紧密集成。

StageWebView 类是一个 HTML 呈现类,与主机 AIR 应用程序仅有非常有限的集成。StageWebView 类加载的内容从不放置在应用程序安全沙箱中,因而无法访问主机 AIR 应用程序中的数据或调用其中的函数。在桌面平台上,StageWebView 类根据 Webkit(HTMLLoader 类也使用它)使用内置 AIR HTML 引擎。在移动平台上,StageWebView 类使用操作系统提供的 HTML 控件。因此,在移动平台上,StageWebView 类与系统 Web 浏览器有着相同的安全性注意事项和漏洞。

TextField 类可以显示 HTML 文本字符串。JavaScript 不可以执行,但文本可以包括链接和外部加载的图像。

有关详细信息,请参阅 避免与安全相关的 JavaScript 错误

配置基于 HTML 的应用程序概述

Frame 和 iframe 提供了一种用于组织 AIR 中的 HTML 内容的便利结构。Frame 提供了一种用于维护数据永久性以及安全使用远程内容的方式。

由于 AIR 中的 HTML 保持其基于页面的正常组织,因此 HTML 环境在 HTML 内容的顶框架“导航”到其他页面时会完全刷新。您可以使用 frame 和 iframe 来维护 AIR 中的数据永久性,方法与维护在浏览器上运行的 Web 应用程序中的数据永久性基本相同。定义顶框架中的主应用程序对象,只要不允许框架导航到新页面,这些对象就会永久保留。使用子级 frame 或 iframe 加载并显示应用程序的临时部分。(除 frame 外,还可以使用多种方式维护数据永久性。其中包括 cookie、本地共享对象、本地文件存储、加密文件存储以及本地数据库存储。)

由于 AIR 中的 HTML 在可执行代码与数据之间保持正常的模糊界限,因此 AIR 将 HTML 环境的顶部框架中的内容放入应用程序沙箱中。在页面的 load 事件之后,AIR 限制 eval() 等任何可以将文本的字符串转换为可执行对象的操作。即使应用程序未加载远程内容,也会强制实施此限制。若要允许 HTML 内容执行这些受限制的操作,必须使用 frame 或 iframe 将内容放入非应用程序沙箱中。 (使用某些依赖 eval() 函数的 JavaScript 应用程序框架时,可能必须运行沙箱子帧中的内容。)有关应用程序沙箱中的 JavaScript 限制的完整列表,请参阅 对不同沙箱中的内容的代码限制

由于 AIR 中的 HTML 能够加载远程潜在不安全内容,因此 AIR 会强制实施同源策略,以防止一个域中的内容与另一个域中的内容进行交互。若要允许应用程序内容与其他域中的内容进行交互,可以设置一个桥,将其用作父级和子级 frame 之间的接口。

设置父子沙箱关系

AIR 会将 sandboxRoot documentRoot 属性添加到 HTML frame 和 iframe 元素中。使用这些属性,您可以将应用程序内容视为其他域中的内容:

属性

说明

sandboxRoot

用于确定在其中放置 frame 内容的沙箱和域的 URL。必须使用 file: http: https: URL 方案。

documentRoot

从中加载 frame 内容的 URL。必须使用 file: app: app-storage: URL 方案。

以下示例对要在远程沙箱中运行的应用程序的 sandbox 子目录中安装的内容以及 www.example.com 域进行了映射:

<iframe 
    src="ui.html"  
    sandboxRoot="http://www.example.com/local/"  
    documentRoot="app:/sandbox/"> 
</iframe>

在不同沙箱或域的父级和子级 frame 之间设置桥

AIR 将 childSandboxBridge parentSandboxBridge 属性添加到任何子级 frame 的 window 对象中。使用这些属性,您可以定义用作父级和子级 frame 之间的接口的桥。每个桥都指向一个方向:

childSandboxBridge childSandboxBridge 属性允许子级 frame 向父级 frame 中的内容公开接口。若要公开接口,需将 childSandbox 属性设置为子级 frame 中的函数或对象。然后,可以从父级 frame 中的内容访问该对象或函数。以下示例显示了子级 frame 中运行的脚本如何向其父级 frame 公开包含函数和属性的对象:

var interface = {}; 
interface.calculatePrice = function(){ 
    return .45 + 1.20; 
} 
interface.storeID = "abc" 
window.childSandboxBridge = interface;

如果此子级内容位于分配的 ID "child" 的 iframe 中,则可以通过读取 frame 的 childSandboxBridge 属性从父级内容来访问接口:

var childInterface = document.getElementById("child").childSandboxBridge; 
air.trace(childInterface.calculatePrice()); //traces "1.65" 
air.trace(childInterface.storeID)); //traces "abc"

parentSandboxBridge parentSandboxBridge 属性允许父级 frame 向子级 frame 中的内容公开接口。若要公开接口,需将子级 frame 的 parentSandbox 属性设置为父级 frame 中的函数或对象。然后,可以从子级 frame 中的内容访问该对象或函数。以下示例显示了父级 frame 中运行的脚本如何向其子级 frame 公开包含 save 函数的对象:

var interface = {}; 
interface.save = function(text){ 
    var saveFile = air.File("app-storage:/save.txt"); 
    //write text to file 
} 
document.getElementById("child").parentSandboxBridge = interface;

使用此接口,子级 frame 中的内容可以将文本保存到名为 save.txt 的文件。但对文件系统不具备任何其他访问权限。通常,应用程序内容向其他沙箱公开的接口应越窄越好。子级内容可以调用 save 函数,如下所示:

var textToSave = "A string."; 
window.parentSandboxBridge.save(textToSave);

如果子级内容尝试设置 parentSandboxBridge 对象的属性,则运行时会引发 SecurityError 异常。如果父级内容尝试设置 childSandboxBridge 对象的属性,则运行时会引发 SecurityError 异常。

对不同沙箱中的内容的代码限制

Adobe AIR 中的 HTML 安全性 主题的简介中已介绍过,运行时强制执行规则,并提供克服 HTML 和 JavaScript 中可能的安全漏洞的机制。本主题列出了这些限制。如果代码尝试调用这些受限制的 API,则运行时将发出错误:“Adobe AIR runtime security violation for JavaScript code in the application security sandbox”(应用程序安全沙箱中存在针对 JavaScript 代码的 Adobe AIR 运行时安全侵犯)。

有关详细信息,请参阅 避免与安全相关的 JavaScript 错误

使用 JavaScript eval() 函数及类似技术的限制

对于应用程序安全沙箱中的 HTML 内容,加载代码后(即在调度 body 元素的 onload 事件以及 onload 处理函数完成执行后),使用可将字符串动态转换为可执行代码的 API 时存在一些限制。这是为了阻止应用程序从非应用程序源(例如潜在不安全网络域)意外插入(及执行)代码。

例如,如果应用程序使用远程源中的字符串数据来写入 DOM 元素的 innerHTML 属性,则字符串中包括的可执行 (JavaScript) 代码可能会执行不安全操作。但是,在加载内容时将远程字符串插入 DOM 不存在风险。

使用 JavaScript eval() 函数时存在一个限制。在加载应用程序沙箱中的代码且处理 onload 事件处理函数之后,只能通过有限的方式使用 eval() 函数。以下规则适用于在从应用程序安全沙箱中加载代码 之后 使用 eval() 函数:

  • 允许表达式中包含文本。例如:

    eval("null"); 
    eval("3 + .14"); 
    eval("'foo'");
  • 允许使用对象文本,如下所示:

    { prop1: val1, prop2: val2 }
  • 禁止 使用 setter/getter 对象文本,如下所示:

    { get prop1() { ... }, set prop1(v) { ... } }
  • 允许使用数组文本,如下所示:

    [ val1, val2, val3 ]
  • 禁止 表达式中包含属性读取,如下所示:

    a.b.c
  • 禁止 调用函数。

  • 禁止 禁止对函数进行定义。

  • 禁止 设置任何属性。

  • 禁止 使用函数文本。

但是,加载代码时,在 onload 事件之前和执行 onload 事件处理函数过程中,这些限制不适用于应用程序安全沙箱中的内容。

例如,加载代码后,以下代码会导致运行时引发异常:

eval("alert(44)"); 
eval("myFunction(44)"); 
eval("NativeApplication.applicationID");

如果应用程序沙箱中允许使用代码,则动态生成的代码(例如在调用 eval() 函数时生成的代码)将导致安全风险。例如,应用程序可能意外执行了从网络域中加载的字符串,而该字符串可能包含恶意代码。例如,这些代码可能会删除或修改用户计算机上的文件。也可能会将本地文件的内容报告给某个不受信任的网络域。

生成动态代码的方式如下所示:

  • 调用 eval() 函数。

  • 使用 innerHTML 属性或 DOM 函数插入加载应用程序目录外部的脚本的 script 标签。

  • 使用 innerHTML 属性或 DOM 函数插入具有内联代码的 script 标签(而不是通过 src 属性加载脚本)。

  • 设置 script 标签的 src 属性可以加载应用程序目录外部的 JavaScript 文件。

  • 使用 javascript URL 方案(如 href="javascript:alert('Test')" 所示)。

  • 使用 setInterval() setTimout() 函数,其中,第一个参数(用于定义要异步运行的函数)为要求值的字符串而不是函数名(如 setTimeout('x = 4', 1000) 所示)。

  • 调用 document.write() document.writeln()

加载内容时,应用程序安全沙箱中的代码只能使用这些方法。

这些限制 不会 阻止将 eval() 和 JSON 对象文本一起使用。这样便可以在 JSON JavaScript 库中使用应用程序内容。但是会限制您使用重载的 JSON 代码(通过事件处理函数)。

对于其他 Ajax 框架和 JavaScript 代码库,需检查框架或库中的代码是否在限制动态生成的代码时起作用。如果不起作用,则需包括在非应用程序安全沙箱中使用框架或库的所有内容。有关详细信息,请参阅 对 AIR 内的 JavaScript 的限制 通过脚本访问应用程序和非应用程序内容 。Adobe 维护一个已知的支持应用程序安全沙箱的 Ajax 框架列表,其网址为 http://www.adobe.com/cn/products/air/develop/ajax/features/

与应用程序安全沙箱中的内容不同,非应用程序安全沙箱中的 JavaScript 内容随时都 可以 调用 eval() 函数来执行动态生成的代码。

访问 AIR API 的限制(针对非应用程序沙箱)

非应用程序沙箱中的 JavaScript 代码无法访问 window.runtime 对象,也无法执行 AIR API。如果非应用程序安全沙箱中的内容调用以下代码,则应用程序会引发 TypeError 异常:

try { 
    window.runtime.flash.system.NativeApplication.nativeApplication.exit(); 
}  
catch (e)  
{ 
    alert(e); 
}

异常类型为 TypeError(未定义的值),由于非应用程序沙箱中的内容无法识别 window.runtime 对象,因此将其认为是未定义的值。

可以使用脚本桥将运行时功能公开给非应用程序沙箱中的内容。有关详细信息,请参阅 通过脚本访问应用程序和非应用程序内容

使用 XMLHttpRequest 调用的限制

应用程序安全沙箱中的 HTML 内容无法使用同步 XMLHttpRequest 方法,在加载 HTML 内容和执行 onLoad 事件期间从应用程序沙箱外部加载数据。

默认情况下,不允许非应用程序安全沙箱中的 HTML 内容使用 JavaScript XMLHttpRequest 对象从非调用请求的域加载数据。 frame iframe 标签可以包括 allowcrosscomainxhr 属性。如果将此属性设置为任何非空值,则会允许帧或 iframe 中的内容使用 JavaScript XMLHttpRequest 对象从域(注意不是调用请求的代码的域)加载数据:

<iframe id="UI" 
    src="http://example.com/ui.html" 
    sandboxRoot="http://example.com/" 
    allowcrossDomainxhr="true" 
    documentRoot="app:/"> 
</iframe>

有关详细信息,请参阅 通过脚本访问不同域中的内容

加载 CSS、frame、iframe 和 img 元素的限制(针对非应用程序沙箱中的内容)

远程(网络)安全沙箱中的 HTML 内容只能从远程沙箱(网络 URL)加载 CSS、 frame iframe img 内容。

只能与本地文件系统内容交互的沙箱、只能与远程内容交互的沙箱或受信任的本地沙箱中的 HTML 内容只能从本地沙箱(而不是应用程序或远程沙箱)加载 CSS、frame、iframe 和 img 内容。

调用 JavaScript window.open() 方法的限制

如果通过调用 JavaScript window.open() 方法创建的窗口中显示了非应用程序安全沙箱中的内容,则窗口的标题将以主(启动)窗口的标题开头,后跟一个冒号字符。无法使用代码将窗口的标题部分从屏幕上删除。

非应用程序安全沙箱中的内容只能成功调用 JavaScript window.open() 方法来响应用户与鼠标或键盘交互而触发的事件。这会阻止非应用程序内容创建可能被欺骗使用的窗口(例如用于仿冒攻击)。此外,鼠标或键盘事件的事件处理函数无法将 window.open() 方法设置为在延迟(例如调用 setTimeout() 函数)后执行。

远程(网络)沙箱中的内容只能使用 window.open() 方法打开远程网络沙箱中的内容。无法使用 window.open() 方法打开应用程序或本地沙箱中的内容。

只能与本地文件系统内容交互的沙箱、只能与远程内容交互的沙箱或受信任的本地沙箱中的内容(请参阅 安全沙箱 )只可使用 window.open() 方法打开本地沙箱中的内容。无法使用 window.open() 打开应用程序或远程沙箱中的内容。

调用受限代码时出现的错误

如果由于这些安全限制而限制在沙箱中使用所调用的代码,则运行时将发出 JavaScript 错误:“Adobe AIR runtime security violation for JavaScript code in the application security sandbox”(应用程序安全沙箱中存在针对 JavaScript 代码的 Adobe AIR 运行时安全侵犯)。

有关详细信息,请参阅 避免与安全相关的 JavaScript 错误

从字符串中加载 HTML 内容时对沙箱的保护

通过 HTMLLoader 类的 loadString() 方法,可以在运行时创建 HTML 内容。但是,如果从不安全的 Internet 来源加载数据,则用作 HTML 内容的数据可能已损坏。由于此原因,默认情况下,使用 loadString() 方法创建的 HTML 不放置在应用程序沙箱中,并且无权访问 AIR API。但是,将 HTMLLoader 对象的 placeLoadStringContentInApplicationSandbox 属性设置为 true,即可将使用 loadString() 方法创建的 HTML 放置在应用程序沙箱中。有关详细信息,请参阅从字符串加载 HTML 内容。