Secure浏览器中安全的跨域通讯

The Architecture Journal

作者:Danny Thorpe

 

摘要

想象一名顾客进入任何虚拟的商店购物,没有任何真实的商品只有一张塑料卡片和图片ID。售货员和顾客不需要使用一种货币,不需要来自同一个国家或使用同一种语言。他们的共同点只是拥有同一个全球通讯系统和全球金融网络,无论顾客去哪里都能够提供金融服务,并且为售货员提供基础体系结构的支持。如果Internet能够为网络冲浪者和网站管理者提供类似的保护和服务来共享信息那将会怎么样呢?

 

内容

IFrame URL 技术
在书签中隐藏数据
发送方标识
发送到发送方
有状态的接收方
思想的应用
用户授权
致谢
资源

开发Web浏览器应用程序很像在Main Street上逛街:有许多商店可以选择,每个商店的橱窗都有琳琅满目的商品可供浏览,但是您什么都拿不到。您残忍的继母-浏览器夫人,每次都会在你趴在橱窗上看的时候,狠狠的拽住约束你的皮带。她口口声声说是为你好,但是你心里想的却是这么短的皮带完全是为了她的方便而不是你的安全。

Web浏览器将不同域的页面隔离以阻止它们彼此对终端用户的记录。在Internet的早期,这种隔离模型是可行的,因为那时候很少有网站在浏览器客户端放置重要的应用逻辑,即使有也只是在它们自己的服务器上访问数据。每个Web服务器都有自己的筒仓,仅包括自身之外内容的HTML连接。

但是今天的互联网不再如从前。互联网的体验已经进化到从多个域聚集数据。这种聚合是由用户对网站的定制和网站通过组合多个不同的数据源来进行增值所驱动的。在这种情况下,Web浏览器的域隔离模型成为客户端Web应用程序开发的严重阻碍。为了避免以上问题,Web应用程序设计者已经开始把越来越多的应用程序逻辑移到Web服务器上,但是这就牺牲了服务器的可扩展性,同时终端用户2GHz,2GB的终端就空闲了下来。

如果个人电脑也像Web浏览器,您就可以将您的数据保存到磁盘,但是您不能利用机器上的其它应用程序或其它的计算机来使用这些数据。如果您打算使用其它的照片编辑器,您就不能编辑您的老照片了。如果您向您原来的照片编辑器制造商抱怨,他们就会不屑一顾的说“我们不知道其它的照片编辑器如何处理您的数据。我们不知道也不信任其它的照片编辑器,您也不应该!我们决不会让您用它们编辑您的照片,因为既然我们为这些照片提供了存储空间,它们其实也是我们的照片”。

除非您记得当初您是使用什么软件建立的文件,否则您甚至都无法找到您的文件。“我用的哪个照片编辑器保存的Stevie的照片?我找不到它们了!”

万一这款前卫的照片编辑器不幸坏掉,再也见不到了,那该怎么办?您的照片也就随之去也!

听起来是否很熟悉?对于使用互联网Web站点和Web应用程序的人来说每天都会发生这种事情。域隔离使您不能在独立的在线商店(与您的音乐播放器制造商没有联系)或零售商店利用您的音乐列表购买类似的歌曲。

域隔离使得很难建立轻量级低基础设施,在集成网络中的不同数据服务器提取切片数据的Web应用程序。您的内部bar.com的子域foo.bar.com像与外部地址如xyz.com隔离一样也与bar.com和bee.bar.com隔离。

但是您并不想冒险取消所有隔离。浏览器严格的域隔离策略所保护的对数据和个人安全的威胁是真实存在的也是危险的。但是经过成熟的考虑和认真的架构,还是有不错的方案既能带给用户便利,又能够保持必要的安全保护。用户可以控制其信息用于某Web站点的时间,内容和多少。目的不是信息在所有的方向都能自由流动,而是使用户不用考虑数据存在位置就能够在需要的时候自由使用数据。

我们需要的是一种方法使得浏览器能够支持合法的跨域数据访问,但是无需牺牲终端用户的安全和对自身数据的控制。

该方向最主要的步骤就是开发由Ian Hickson组织的标准提案,以扩展xmlHttp请求使用服务器请求时基于域的opt-in/opt-out来支持跨域连接。(参见资源)。如果这种想法通过了同行评议,并且被主要浏览器执行,这就为降低合法使用的跨域阻碍,同时仍然保护合法使用带来了希望。但是,在现实中,要想使这一提案被主要浏览器执行并在业内普遍使用需要几年的时间。

那现在我们能做些什么呢?目前所有的浏览器都支持一种行为模式,就是允许一个浏览器域上下文的JavaScript代码观察同一浏览器实例中另一域上下文中JavaScript的更改。例如,对iframe宽度和高度属性的更改在iframe内部和外部都可以观察到。另一个例子是iframe.src属性. iframe外部代码不能读取iframe.src的URL属性,然后却可以写iframe.srcURL。这样iframe外部代码可以通过iframe.src的URL向其内部传送数据。

自从iframe被引入HTML,Web设计者就开始使用URL,但是,对其的使用都是基本的,基于目标的,随意的组合在一起。更糟的是,通过iframe src URL传送数据会产生一个漏洞利用程序矢量,允许邪恶代码通过向iframe抛垃圾信息而破坏您的Web应用程序。在这种情况下任何代码都可以写入iframe 的src属性,接收到信息的iframe不知道URL数据的来源。在大多数情况下,不清楚来源的数据是不被信任的。

本文主要探讨由Windows Live Developer Platform group开发的安全客户端跨领域数据通道相关问题和解决方案技术。

 

IFrame URL 技术

iframe是一种HTML元素,用于封装和展示一个完整的HTML文档,允许您在一个HTML文件中展示另一个HTML。我们称iframe的父亲为外部页面或主页面,而称iframe的内容为内部页面。iframe的内部页面是通过向iframe的src属性指定一个URL来确定的。

当iframe的源URL与其外部页面有同样的域名时,主页JavaScript可从通过iframe的DOM导航看到它所有的内容。同样的,iframe也可以通过它的父连接进行导航看到主页中DOM同级及其属性。然而,当iframe的源URL的域与主页不同,主页就不能看到iframe的内容,iframe也无法看到主页内容。

即使主页不能读iframe元素的src属性,它仍然能够对它进行写操作。主要不知道iframe正在展现什么,但是却可以强制其展现其它内容。每当一个新的URL赋予iframe的src属性,iframe就会执行下载页面所需的通用步骤,包括激活onLoad事件。

我们列出了从主页到URL上iframe传送数据所需的所有内容(见图1)。在域foo.com中的主页就可以在bar.com域现有文档URL最后放置一个URL编码的数据包。该数据在URL中使用?作为查询参数(http://bar.com/receiver.html?datadatadata)或是用#作为书签(http://bar.com/receiver.html#datadatadata)。这两类URL类型有很大区别,我们下面会进行探讨。

1: iframe URL 数据传送

page1

 

“域隔离使得很难建立轻量级低基础设施,在集成网络中的不同数据服务器提取切片数据的Web应用程序。. 但是您并不想冒险取消所有隔离。”

主页为iframe的src属性指定URL,iframe下载页面,激活页面的onLoad事件处理程序。iframe页面的onLoad事件处理程序可以查看它自身的URL,找到内嵌的数据包,对它进行解码从而决定下一步做什么。

这是最简单的URL数据传送技术。主页根据已知文档URL文档url + data payload,指定给iframe的src属性,iframe在onLoad事件处理程序中被激活,接受数据负载。您还需要知道些什么?实际上有很多。

这项简单技术有许多地方需要警惕:

•   没有确认回执。主页不知道iframe是否成功的接收了信息。

•   消息覆盖。主页不知道iframe什么时候完成对上一个消息的处理,因此不知道何时发送下一个消息才安全。

•   容量限制。一个URL有长度限制,根据不同的浏览器该限制也不同。Firefox支持大约40k左右的URL,而IE限制在4k。超过这一限制的内容将会被删去或忽略。

•   数据来源不明。 iframe不知道谁将数据发送到它的URL。数据可能是发送自友好主页foo.com,也可能来自evil.com攻击bar.com希望进行破坏。.

•    无回复.没有方法使 iframe的脚本将数据返回到主页。

•   缺少上下文。因为每来一个消息,页面都重新下载,iframe内部就不能获得消息的全局状态。

 

在书签中隐藏数据

我们应该使用?或#将数据加到iframeURL结尾吗?虽然表面看起来没有什么问题,但是浏览器处理带有查询参数的URL和带有书签的URL的方式是非常不同的。两个具有同样数据库路径但是不同查询参数的URL被视为不同的URL。它们单独出现在浏览器历史列表中,并各自进入浏览器页缓存,将会产生各自的网络请求。

URL书签被设计为指向页面内特定的定位标记。浏览器认为两个具有同样数据库路径但是在#之后带有不同书签的URL被视为相同的URL,浏览器历史和缓存也是同样的情况。不同的书签只是指向同页面(URL)中不同的部分,但是仍然是同一个页面。

URL书签被设计为指向页面内特定的定位标记。浏览器认为两个具有同样数据库路径但是在#之后带有不同书签的URL被视为相同的URL,浏览器历史和缓存也是同样的情况。不同的书签只是指向同页面(URL)中不同的部分,但是仍然是同一个页面。

 

“我们应该使用?或#将数据加到iframeURL结尾吗?虽然表面看起来没有什么问题,但是浏览器处理带有查询参数的URL和带有书签的URL的方式是非常不同的。”

 

浏览器认为URL http://bar.com/page.html\#one, http://bar.com/page.html\#two和http://bar.com/page.html\#three都相当于http://bar.com/page.html。如果我们使用查询参数,浏览器就会认为是三个不同的URL和三个不同的网络行程。然而,使用书签,我们最多只有一条网络行程;本地浏览器缓存就会被相应的请求填满。(见图2)

**2:**书签URLs等同的缓存

page2

在需要使用同一个URL,通过iframe的URL传送大量信息的情况下,书签是最好的。URL书签部分的负载不会出现在浏览器历史或页面缓存中。并且,在原页面下载到缓存后,数据负载决不会通过网络。

在主页和iframe之间传送的数据不会被其它主页上的DOM元素看到,因为iframe与主页处在不同的域。数据不出现在浏览器缓存中,数据也不会跨过网络,因此可以说数据包只能被接收的iframe或bar.com域其它页面观察到。


发送方标识

可能使用简单的iframeURL数据发送技术最大的安全问题在于不确定数据来源。将发送方的名称或某些形式的应用程序ID嵌入不是解决方法,因为这些很容易就能被恶意冒充者复制。我们需要一个隐性标识消息发送方的方法,使其不会轻易被复制。

大多数首先想到的方法是使用某种形式的加密,利用只有发送方和接收方拥有的密钥来解决。这种方法当然能够完成该任务,但是这是一个相当麻烦的解决方案,特别是涉及JavaScript时。

还要另一种方法,就是利用浏览器环境中域名识别的重要性。

如果我可以用你的域名发送秘密消息给你,然后我再将此消息接收为数据包的部分,那我就可以推测数据包是来自你的域。

该消息来自第三方的唯一途径就是你的域已经被泄漏了,用户的浏览器已经被泄漏了,或者我的DNS已经被泄漏了。如果你的域或你的浏览器已经被泄漏了,那后果不堪设想。如果DNS中毒,您可以使用https来验证服务器对于给定域名请求的回答。

如果发送方给接收方一个密钥,接收方给发送方一个密钥,两个密钥都在每个数据包通过iframe URL 数据通道传送,那么双方都对每个消息的来源信任。来自evil.com的攻击可以很容易被识别和去除。这种密钥交换是SSL/https三次握手的启发而想到的。

这些密钥并不需要多么复杂或加密,因为通过iframe URL 数据通道传送的数据包第三方不可见。随机数作为密钥就已经足够,但是有一个缺点:JavaScript随机数发生器(Math.random())加密功能并不强,因此很有可能产生可以预测的数字串。Firefox提供一个加密功能较强的随机数发生器(crypto.random()),但是IE没有。因此,在我们的执行中,我们选择在Web服务器产生强壮的随机数,将它们发送到需要的客户端。

 

发送到发送方

大多数与iframe URL数据传送技术相关的问题归结于回答产生。确认包需要接收方向发送方发送一个回答。交换密钥需要双方都回答。消息限制和将大型数据负载分成多个较小消息需要确认回执。

page3

那么,iframe是如何与主页通讯的呢?不是向上,而是向下。iframe不能给它的父亲指定任何消息,因为它们位于不同的域上下文。但是bar.com iframe(A)能够包含另一个iframe(B),A可以指定B的src属性为主页(foo.com)域的URL,foo.com主页包括bar.com iframe(A),而iframe(A)又包含foo.com iframe(B)。

不错,但是那个内部iframe有什么作用呢?它不能与它的父亲bar.com iframe实现什么功能,但是再上一层,你就会发现好处:B的父亲的父亲是主页foo.com,B的页面在foo.com中,而B.parent.parent在foo.com中,因此B可以访问主页的任何内容,调用主页上下文的JavaScript函数。

主页可以通过将URL写入A的src属性发送数据,A可以处理数据,然后通过将URL写入B的src属性向主页发送确认。B在它的onLoad事件时被激活,向它的父亲的父亲,也就是主页发送消息。确认经过单向相连的通道转了一圈,这种方式可能让Felix Klein和数学家感到可笑。

 

有状态的接收方

为保持有多个消息发送到iframe的bar.com上下文的全局状态,使用两个iframe。利用一个iframe作为无状态消息接收方,每接收一个消息就重新下载,并失去状态。将bar.com一方的有状态逻辑放置在其它iframe中。将消息器iframe页面逻辑减少到向有状态bar.com iframe传送数据所需的最小量。

 

“如果我可以用你的域名发送秘密消息给你,然后我再将此消息接收为数据包的部分,那我就可以推测数据包是来自你的域。如果DNS中毒,您可以使用https来验证服务器对于给定域名请求的回答。”

一个iframe不能将所有它的父亲的儿子都列举出来以查看其它bar.com同级,但是如果它知道同级iframe的名字,它可以使用window.parent.frames[]来查看该同级iframe。每当它重载以接收URL上的新数据,消息器iframe能够使用window.parent.frames[]查看它的有状态bar.com同级,并调用有状态iframe的函数来向有状态iframe传送新的消息数据。这样,浏览器内存中的bar.com域上下文将多条消息聚集成消息块来重建比浏览器要求最大长度大的数据负载。

 

思想的应用

Windows Live 开发者平台团队已经将这一思想应用于JavaScript“通道”库。这些跨领域的通道用于执行Windows Live Contacts 和 Windows Live Spaces Web controls (http://dev.live.com),试图在第三方Web页面执行live.com域上下文中的安全iframe。这一控制为第三方网站提供了用户控制的对其Windows Live数据的访问入用户联系表或Spaces照片集。通道对象支持发送跨iframe域界限的任意大的数据,确认回执,消息限制,消息块和发送方标识都发生在内部。

我们的目标是将此通道代码写入可重用的代码库,使内部的微软工作伙伴和第三方Web开发人员都可以使用。尽管这些代码在它当前的背景中运行的很好,我们在自我诊断和故障处理方面仍然有很多工作要做;如果您对通道端点配置正确的话,它能工作的很好,但是一旦首次建立时不正确,那简直就是一场恶梦。主要的问题是浏览器本身—当浏览器不愿意向您展示墙那边的情况,那想知道不同域上下文的内容是一个很大的挑战。

 

用户授权

40年前,一个在美国主要街道逛街的顾客,必须努力让零售商接收付款,如果没有现金(或是很多现金),您就没有那么幸运了。如果您有外国货币,您必须找一家大银行来兑换当地的货币。商店很少接收外地货币,而且信用卡也只提供给当地居民。

如今,顾客和零售商都共享全球通讯系统和全球银行网络,这就允许顾客将银行服务带在身边,而零售商也不会失去以往有可能失去的生意。银行网络提供对零售商基础体系结构的支持,帮助其进行货币兑换,规避信用风险和减少欺诈带来的损失。

那为什么互联网就不能为网上冲浪者和网站拥有者提供类似的保护和授权呢?在您从一个网站到另一个网站时,将您的数据和经验带在身边(就像您购物时银行卡带给您的银行服务一样),根据自己的意愿向网站发布信息。互联网正在向这个方向发展,唯一的问题是如何发展的更快更好。

 

致谢

感谢Scott Isaacs原创的iframe URL 数据传送概念。感谢Yaron Goland 和 Bill Zissimopoulos对早期通道代码执行和调试所作的贡献,感谢Gabriel Corverra 和Koji Kato最近所进行的修改。“这绝对是疯了,但是却可能有用!”

 

资源

XMLHttpRequest 2, Ian Hickson

http://www.mail-archive.com/public-webapi@w3.org/msg00341.html

http://lists.w3.org/Archives/Public/public-webapi/2006Jun/0012.html

Anne van Kesteren’s weblog
http://annevankesteren.nl/2007/02/xxx

 

作者简介

Danny Thorpe 是Windows Live开发者平台团队的一名开发人员,他的徽标写的是“Principal SDE” ,但是他更喜欢“Windows Live Quantum Mechanic”,因为他大部分时间都是在排除移植时的一些顽固障碍。他曾经在Google的“undisclosed browser technology”工作,在这之前是Borland的首席科学家和Delphi compiler主架构师。在Borland,他有幸在Anders Hejlsberg, Chuck Jazdzewski, Eli Boling,以及许多其他Borland传奇人物的指导下工作。在进入Borland之前他太年轻了,什么都记不得了。请访问他的博客 https://blogs.msdn.com/dthorpe