Adobe AIR 的 HTML 安全性

Adobe AIR 1.0 以及更新的版本

本主題說明 AIR HTML 安全性架構以及如何使用 iframes、frames 和安全執行程序橋接,以安裝 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 類別具有與系統網頁瀏覽器相同的安全性考量與弱點。

TextField 類別可以顯示 HTML 文字的字串。無法執行 JavaScript,但是文字可以包括連結與外部載入的影像。

如需詳細資訊,請參閱 避免發生與安全性有關的 JavaScript 錯誤

設定 HTML 類型應用程式的概觀

Frame 和 iframe 提供便利的結構,可供在 AIR 中組織 HTML 內容。Frame 提供了方法,可維護資料持續性並可安全地配合遠端內容運作。

由於 AIR 中的 HTML 保留其正常的頁面類型組織,如果 HTML 內容的最上端框架「瀏覽」至不同的頁面,HTML 環境會整個重新整理。您可以使用 frame 和 iframe 來維護 AIR 中的資料持續性,與在瀏覽器中執行之網路應用程式的方式相同。只要您不允許瀏覽至新頁面,就可以在最上端框架中定義您的主應用程式物件。請使用子 frame 或 iframe 載入並顯示應用程式的 transient 部分 (您有各種不同的方式可以維護與框架一起使用或代替框架使用的資料持續性。其中包括 Cookie、本機共享物件、本機檔案儲存、加密檔案存放區,以及本機資料庫儲存)。

因為 AIR 中的 HTML 會保留其可執行程式碼與資料之間正常而模糊的界線,所以 AIR 會將 HTML 環境內最上層頁框的內容放到應用程式安全執行程序中。在頁面 load 事件發生後,AIR 會限制任何可將文字字串轉換成可執行物件的操作 (例如 eval() )。即使在應用程式不載入遠端內容時,這項限制也會強制執行。若要允許 HTML 內容執行這些限制的作業,必須使用 frame 或 iframe 將內容放入非應用程式安全執行程序中 (當使用某些依賴 eval() 函數的 JavaScript 應用程式架構時,就可能需要在執行程序子框架中執行內容)。如需有關應用程式安全執行程序中 JavaScript 之限制的完整清單,請參閱 不同安全執行程序中內容的程式碼限制

由於 AIR 中的 HTML 保留其可載入可能不安全之遠端內容的功能,AIR 會強制執行相同來源的原則,以防止一個網域中的內容與其它網域中的內容互動。若要允許應用程式內容與其它網域中的內容彼此互動,可以設定橋接,做為父框架與子框架之間的介面。

設定父子安全執行程序關係

AIR 會將 sandboxRoot documentRoot 特質加入 HTML 的 frame 和 iframe 元素。這些特質可以讓您將應用程式內容視為來自其它網域的內容:

特質

說明

sandboxRoot

用來判斷要放置框架內容之安全執行程序和網域的 URL。這裡必須使用 file: http: https: URL 配置。

documentRoot

從中載入框架內容的 URL。您必須使用 file: app: app-storage: URL 配置。

下列範例會對應安裝在應用程式之 sandbox 子目錄中的內容,以便在遠端安全執行程序和 www.example.com 網域中執行:

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

設定不同安全執行程序或網域中父和子框架之間的橋接

AIR 會將 childSandboxBridge parentSandboxBridge 屬性加入至任何子框架的 window 物件。這些屬性可以讓您定義橋接,以做為父框架與子框架之間的介面。每個橋接都是單向的:

childSandboxBridge childSandboxBridge 屬性允許子框架向父框架中的內容公開介面。若要公開介面,請將 childSandbox 屬性設定為子框架中的函數或物件,然後,您就能從父框架中的內容存取物件或函數。下列範例會示範在子框架中執行的指令碼如何將包含函數和屬性的物件公開給其父輩:

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 屬性允許父框架向子框架中內容公開介面。若要公開介面,請將子框架的 parentSandbox 屬性設定為父框架中的函數或物件。然後,您就能從子框架中的內容存取物件或函數。下列範例會示範父輩 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;

使用此介面,子框架中的內容可以將文字儲存至 save.txt 檔案中。但是,它對檔案系統沒有任何其它存取權限。一般來說,應用程式內容應該盡可能向其它安全執行程序公開限制最多的介面。子系內容可以呼叫 save 函數,如下所示:

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

如果子內容嘗試設定 parentSandboxBridge 物件的屬性,執行階段就會擲出 SecurityError 例外。如果父內容嘗試設定 childSandboxBridge 物件的屬性,執行階段就會擲出 SecurityError 例外。

不同安全執行程序中內容的程式碼限制

Adobe AIR 的 HTML 安全性 主題簡介已說明,執行階段會強制執行規則並提供機制,以克服 HTML 和 JavaScript 中可能有的安全性弱點。本主題會列出這些限制。如果程式碼嘗試呼叫這些受限制的 API,執行階段會擲出錯誤,並顯示訊息「應用程式安全執行程序中的 JavaScript 程式碼造成 Adobe AIR 執行階段安全性違規」。

如需詳細資訊,請參閱 避免發生與安全性有關的 JavaScript 錯誤

使用 JavaScript eval() 函數及類似技巧的限制

對於應用程式安全執行程序中的 HTML 內容,在載入程式碼之後使用可以動態方式將字串轉換成可執行程式碼的 API 有其限制 (在傳送 body 元素的 onload 事件,而且 onload 處理常式函數已完成執行之後)。其目的在於防止應用程式不小心從非應用程式來源 (例如可能不安全的網路網域) 插入 (並執行) 程式碼。

例如,如果您的應用程式使用遠端來源的字串資料來寫入至 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 函數,插入指令碼標籤 (載入應用程式目錄之外的指令碼)。

  • 使用 innerHTML 屬性或 DOM 函數,插入具有內嵌程式碼的指令碼標籤 (而不是經由 src 特質載入指令碼)。

  • 設定 script 標籤的 src 特質,以載入應用程式目錄之外的 JavaScript 檔案。

  • 使用 javascript URL 配置 (如 href="javascript:alert('Test')" )。

  • 使用 setInterval() setTimout() 函數,其中第一個參數 (定義函數以非同步方式執行) 是字串 (要進行評估) 而不是函數名稱 (如 setTimeout('x = 4', 1000) )。

  • 呼叫 document.write() document.writeln()

載入內容時,應用程式安全執行程序中的程式碼只能使用這些方法。

這些限制「不」會阻止搭配 JSON 物件常值使用 eval() 。這可以讓應用程式內容配合 JSON JavaScript 元件庫運作。但是,您會受到限制,因此無法使用多載的 JSON 程式碼 (搭配事件處理常式)。

對於其它 Ajax 架構和 JavaScript 程式碼元件庫,請檢查架構或元件庫中的程式碼是否能在以動態方式產生的程式碼上依照這些限制來運作。如果不是,請包含使用非應用程式安全執行程序中架構或元件庫的任何內容。如需詳細資訊,請參閱 AIR 中 JavaScript 的限制 在應用程式與非應用程式內容之間編寫指令碼 。Adobe 提供一份清單,當中列出可支援應用程式安全執行程序的 Ajax 架構,請參閱 http://www.adobe.com/products/air/develop/ajax/features/

非應用程式安全執行程序中的 JavaScript 內容與應用程式安全執行程序中的內容不同,此 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 內容時及 onLoad 事件期間,應用程式安全執行程序中的 HTML 內容不能使用同步 XMLHttpRequest 方法,從應用程式安全執行程序之外載入資料。

根據預設,非應用程式安全執行程序中的 HTML 內容不允許使用 JavaScript XMLHttpRequest 物件,從呼叫要求的網域之外其它網域載入資料。 frame iframe 標籤可以包含 allowcrosscomainxhr 特質。設定此特質至任何非 null 值會允許 frame 或 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 錯誤:「應用程式安全執行程序中的 JavaScript 程式碼造成 Adobe AIR 執行階段安全性違規」。

如需詳細資訊,請參閱 避免發生與安全性有關的 JavaScript 錯誤

從字串載入 HTML 內容時的安全執行程序保護

HTMLLoader 類別的 loadString() 方法可讓您在執行階段中建立 HTML 內容。不過,如果您用來做為 HTML 內容的資料是從不安全的網際網路來源載入,則資料有可能損毀。基於這個原因,根據預設,以 loadString() 方法建立的 HTML 不會放在應用程式安全執行程序中,而且無法存取 AIR API。不過,您可以將 HTMLLoader 物件的 placeLoadStringContentInApplicationSandbox 屬性設為 true,將透過 loadString() 方法建立的 HTML 放到應用程式安全執行程序中。如需詳細資訊,請參閱 從字串載入 HTML 內容