浏览器的同源策略
同源策略限制从一个源加载的文档或脚本如何与来自另一个源的资源进行交互。这是一个用于隔离潜在恶意文件的关键的安全机制。
一个源的定义
如果协议,端口(如果指定了一个)和域名对于两个页面是相同的,则两个页面具有相同的源。
下表给出了相对http://store.company.com/dir/page.html同源检测的示例:
URL 结果 原因
http://store.company.com/dir2/other.html 成功
http://store.company.com/dir/inner/another.html 成功
https://store.company.com/secure.html 失败 不同协议 ( https和http )
http://store.company.com:81/dir/etc.html 失败 不同端口 ( 81和80)
http://news.company.com/dir/other.html 失败 不同域名 ( news和store )
源的继承
来自about:blank和javascript:URL的内容从加载URL的文档继承原始文档,因为URL本身不提供任何有关原点的信息。 data:URLs获得一个新的,空的安全上下文。
IE 例外
当涉及到同源策略时,Internet Explorer有两个主要的例外
授信范围(Trust Zones):两个相互之间高度互信的域名,如公司域名(corporate domains),不遵守同源策略的限制。
端口:IE未将端口号加入到同源策略的组成部分之中,因此 http://company.com:81/index.html 和http://company.com/index.html 属于同源并且不受任何限制。
这些例外是非标准的,其它浏览器也未做出支持,但会助于开发基于window RT IE的应用程序。
源的更改
页面可能会更改其自己的来源,但有一些限制。脚本可以将
document.domain
的值设置为其当前域或其当前域的超级域。如果将其设置为其当前域的超级域,则较短的域将用于后续原始检查。例如,假设文档中的一个脚本在 http://store.company.com/dir/other.html 执行以下语句:
document.domain = "company.com";
这条语句执行之后,页面将会成功地通过对
http://company.com/dir/page.html
的同源检测。而同理,company.com
不能设置
document.domain
为
othercompany.com
.
浏览器单独保存端口号。任何的赋值操作,包括document.domain = document.domain都会以null值覆盖掉原来的端口号。因此
company.com:8080页面的脚本不能仅通过设置document.domain = "company.com"就能与company.com通信。赋值时必须带上端口号,以确保端口号不会为null。
注意:使用document.domain允许子域安全访问其父域时,您需要设置document.domain在父域和子域中具有相同的值。这是必要的,即使这样做只是将父域设置回其原始值。否则可能会导致权限错误。
跨源网络访问
同源策略控制了不同源之间的交互,例如在使用XMLHttpRequest
是一个API, 它为客户端提供了在客户端和服务器之间传输数据的功能。它提供了一个通过 URL 来获取数据的简单方式,并且不会使整个页面刷新。这使得网页只更新一部分页面而不会打扰到用户。XMLHttpRequest 在 AJAX 中被大量使用。")
或
<img>
标签时则会受到同源策略的约束。交互通常分为三类:
通常允许进行跨域写操作(Cross-origin writes)。例如链接(links),重定向以及表单提交。特定少数的HTTP请求需要添加
preflight
通常允许跨域资源嵌入(Cross-origin embedding)。之后下面会举例说明。
通常不允许跨域读操作(Cross-origin reads)。但常可以通过内嵌资源来巧妙的进行读取访问。例如可以读取嵌入图片的高度和宽度,调用内嵌脚本的方法,或
availability of an embedded resource
。
如何允许跨源访问
使用
允许跨源访问。
如何阻止跨源访问
-
阻止跨域写操作,只要检测请求中的一个不可测的标记(CSRF token)即可,这个标记被称为
Cross-Site Request Forgery (CSRF)
标记。必须使用这个标记来阻止页面的跨站读操作。
阻止资源的跨站读取,需要保证该资源是不可嵌入的。阻止嵌入行为是必须的,因为嵌入资源通常向其暴露信息。
-
阻止跨站嵌入,确保你得资源不能是以上列出的可嵌入资源格式。多数情况下浏览器都不会遵守Conten-Type消息头。例如,如果你在<script>标签中嵌入HTML文档,浏览器仍将HTML解析为Javascript。
When your resource is not an entry point to your site, you can also use a CSRF token to prevent embedding.
跨源脚本API访问
Javascript的APIs中,如
[iframe.contentWindow
],[window.parent
][window.open
]根据指定的参数,将一个资源加载到一个新的浏览上下文(如一个窗口)或一个已经存在的浏览上下文中。")和[window.opener
]返回打开当前窗口的那个窗口的引用.")
允许文档间直接相互引用。当两个文档的源不同时,这些引用方式将对[Window]和 [Location]对象的访问添加限制。可以使用[window.postMessage
] window.postMessage() 方法可以安全地实现跨源通信。通常,对于两个不同页面的脚本,只有当执行它们的页面位于具有相同的协议(通常为https),端口号(443为https的默认值),以及主机 (两个页面的模数 Document.domain设置为相同的值) 时,这两个脚本才能相互通信。window.postMessage() 方法提供了一种受控机制来规避此限制,只要正确的使用,这种方法就很安全。")
作为替代方案,提供跨域文档间的通讯。
位置
Specification:
http://www.whatwg.org/specs/web-apps/current-work/multipage/browsers.html#security-location.
允许以下对位置属性的跨源访问:
Methods |
---|
location.replace |
Attributes | |
---|---|
URLUtils.href |
只写. |
某些浏览器允许访问比规范允许的更多属性
跨源数据存储访问
存储在浏览器中的数据,如localStorage和IndexedDB,以源进行分割。每个源都拥有自己单独的存储空间,一个源中的Javascript脚本不能对属于其它源的数据进行读写操作。
window.name属性可以用来临时存储数据,可以跨域访问。
Cookies使用不同的源定义方式。一个页面可以为本域和任何父域设置cookie,只要是父域不是公共后缀(public suffix)即可。Firefox和Chrome使用Public Suffix List决定一个域是否是一个公共后缀(public suffix)。不管使用哪个协议(HTTP/HTTPS)或端口号,浏览器都允许给定的域以及其任何子域名(sub-domains)来访问cookie。设置cookie时,你可以使用Domain,Path,Secure,和Http-Only标记来限定其访问性。读取cookie时,不会知晓它的出处。尽管使用安全的https连接,任何可见的cookie都是使用不安全的连接设置的。