在下面的例子中看看场景是如何可以被子SWF访问,但是却不能调用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.
场景的这种所有者关系非常操蛋,因为我们经常需要对场景对象监听鼠标或者键盘事件,比如检测键盘按下或者检测鼠标在物体外部释放点击。在这种情况下,单靠子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的路径添加到本地的安全文件列表里。
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能够同时访问本地和网络。
本地安全域
关于本地信任机制还有一点需要注意的是即使是本地受信的文件,仍然不能把处于外部沙箱的内容加载入本地沙箱。
大多数Flash内容都是为网络创建的,所以通常不需要完全理解本地的安全沙箱机制。但是开发和测试是一个例外。Flash编辑器在测试的时候就是把SWF放在一个本地受信的安全沙箱里面。这有可能会造成与真正发布的SWF情况不同的结果,因为两者的安全沙箱不同。比如你测试的时候可以从没有授信的外部域读取内容,而真正发布到网站上的SWF却无法加载。所以当你测试的时候要注意,你所看到的结果和最终发布的版本有可能不一样。
你可以到Flash Player Developer Center(adobe.com)的Security section查看更详细的安全相关信息。