25 Aug 2015

理解同源策略, 限制, 目的与替代方案

每个程序员都或多或少会向不同的域获取/发送数据. 最近, 我在开发一个书签工具, 需要在公司的站点与其他域间进行信息交互. 当两个域之间需要进行交互时, 理解一下同源策略就很有必要了. 维基百科是这么描述同源策略的:

“同源策略允许运行在同一网站上的脚本可以毫无限制的访问双方的方法与属性, 但是会组织不同网站间的大多数方法与属性的访问”

需要了解的是: 存在2中同源策略. “DOM 同源策略” 负责在不同窗口下通过 DOM 进行读写的部分. ““XmlHttpRequest 同源策略”负责不同窗口间的 Ajax 通信.


DOM 同源策略

DOM 同源策略禁止读取/写入到与当前地址不同的域. 这个特性预防了很多可能被用于窃取用户信息的攻击. 下面是一个简单的例子:

1.MaliciousSite.com 有一个到 mybank.com 的链接 2.用户点击这个链接时, 通过 window.open 在新窗口打开 mybank.com 3.MaliciousSite.com 可以接着通过 mybank.com 的 window 对象的引用向 mybank.com 执行读写操作


XmlHttpRequest 同源策略

XmlHttpRequest 同源策略禁止通过 XmlHttpRequest 向不同网站发送 HTTP 请求, 因此不同站点间常规的带端点的 Ajax 时是不可能实现的. 关于这部分的解决方案, 我将在收获进行讨论. 相比于DOM, XmlHttpRequest 同源策略真正的防止了不同路线的攻击. XmlHttpRequest 同源策略会尝试阻止跨站请求伪造攻击 (CSRF).

当 HTTP 请求被创建后, web 浏览器会发送属于被请求域的 cookies. 尤其是, 网站通常使用一个认证登陆的 cookie. 基于此点, 如果没有 XmlHttpRequest 同源策略, CSRF 攻击会是这样的:

  1. 用户访问了一个恶意网站 MaliciousSite.com
  2. MaliciousSite.com 盲猜用户在另一个选项卡登陆了 MyBank.com
  3. MyBank.com 通过 web services 向已认证的用户暴露了数据
  4. MaliciousSite.com 通过 MyBank.com 的 web services 来获取信息. MyBank.com 上的 web services 返回请求的数据, 因为用户已经被认证了, 并且认证信息的 cookie 也通过 Ajax 请求发送了过来.

替代方案

如同”两种”同源策略一样, 也有两种对应的”迂回”的方式, 以允许窗口间的交互. 接下来让我们看一下这些方法, 以及它们是如何合法的通信并且没有引入同样的安全漏洞.


替代方案 - DOM 同源策略

为了在 DOM 同源策略下正常工作, 应该使用 window.postmessage(). 这个方法现在是 HTML 标准的一部分了, 但是已经存在了很久了. 它适用于IE8+, 以及所有现代版本的 FF 和 Chrome. windows.postmessage 的用法在网络上有很详尽的文档, 大致归纳如下:

  1. 获取需要交互的 window 对象的引用, 例如: var win = window.frames[0].window
  2. window 接受消息, 为将被发送的消息创建事件监听器
  3. 发送者调动 win.postmessage(dataMessage)
  4. 对于双向沟通, 双方都将建立事件监听器

window.postmessage 不允许未授权情况下共享信息. 使用 window.postmessage 要求双方约定好消息格式, 并且需要容器来接受发送过来的消息. 在前面的例子中, 即使 mybank.com 已经建立了事件监听器, myBank.com 也绝不会接收来自 mybank.com 的消息, 数据共享的方法会无视未知地址发送来的消息.


替代方案 - XmlHttpRequest 同源策略

处理 XMLHttp 同源策略会有一些不同的选项. 首先是, 如果你想使用另一个域名下的数据服务, 可以简单的在你的域上创建一个代理服务, 在其中调用其他域下的服务并且回传数据. 这引入了多余的服务端的编码, 但是它却不需要与其他域的协作. 另一个我将提到的技术则涉及其他域下的web service是如何构建的. 如果其他域并不”合作”, 可能开发一个代理服务就是唯一的选择了.

如果网站想对外暴露出可以从其他域通过 Ajax 访问的服务, 有两个选择, JSONP 和 CORS. JSONP 利用了 JavaScript 可以来自不同域的原理. JSONP 服务的请求被格式化为 JavaScript 文件的请求. 如果网站返回 JSONP, 事实上是返回了一段包含一个函数的 JS 代码, 执行函数后, 返回 JSON 数据. JSONP 技术是可用的, 但是它是一个 “漏洞”. 尽管不太可能, 但JSONP 服务理论上仍然是一个 CSRF 攻击媒介. JSONP 为的是实现跨域请求, JSONP 服务需要不同实现方式. 正因如此, 通过 JSONP 服务意外泄露敏感数据并不是一个真正的问题.

CORS是一个相对较新的标准, 它以更适当的方式实现了跨域 Ajax. CORS 代表 Cross Origin Request Sharing. 只要在目标服务器允许的情况下, 所有现代浏览器都可以创建 Ajax 请求到其他域. 安全握手通过 HTTP headers 进行. 当客户端创建了一个跨域的请求, 它包含在 HTTP Header - Origin 内, 其中声明了目标服务器的域名. 如果服务端允许跨域请求, 它就必须把 Origin 返回到 HTTP response header – Access-Control-Allow-Origin 内. 目标服务器可以建立一个安全策略来接收来自任何地方或仅特定域名下的的请求. 因此, 如果使用 JQuery , 客户端没有额外的编码是必要的. 我们可以发现 CORS 也使得 CSRF 攻击的可能性极小. 如果一个网站在web service 内检查身份验证令牌,那么它肯定不会对非受信域添加 origin header.


参考

13 Dec 2012 ▸ Understanding the Web-Same Origin Policy, Restrictions, Purpose, and Workarounds



Tags:
0 comments