同源策略限制从一个源加载的文档或脚本如何与来自另一个源的资源进行交互,这是一个用于隔离潜在恶意文件的关键的安全机制
一个源的定义
如果协议,端口(如果指定了一个)和域名对于两个页面是相同的,则两个页面具有相同的源
下面给出了相对http://store.company.com/dir/page.html 同源检测的示例
URL | 结果 | 原因 |
---|---|---|
http://store.company.com/dir2/other.html | True | |
http://store.company.com/dir/inner/another.html | True | |
https://store.company.com/secure.html | False | 不同协议(https和http) |
http://store.company.ocm:81/dir/etc.html | False | 不同端口(81和80) |
http://news.company.com/dir/other.html | False | 不同域名(news和store) |
还有参考文件的源定义
源的继承
来自 about:blank 和 javascript:URL 的内容从加载URL的文档继承原始文档,因为URL本身不提供任何有关原点的信息。data:URLs 获得一个新的,空的安全上下文
注意: 在Gecko 6.0 之前,如果用户在位置栏中输入data URLs,data URLs将继承当前浏览器窗口中网页的安全上下文。
IE 例外
当涉及到同源策略是,Internet Explorer有主要的两个例外
- 授信范围:两个相互之间高度互信的域名,如公司域名,不遵守同源策略的限制
- 端口:IE未将端口号加入到同源策略的组成部分之中,因此 http://company.com:81/index.html 和 http://company.com/index.html 属于同源并且不受任何限制
源的更改
页面可能会更改自己的来源,但有一些限制。脚本可以将 document.domain
的值设置为其当前域或其当前域的超级域,如果将其设置为其当前域的超级域,则较短的域将用于后续原始检查。
例如:假设文档中的一个脚本在 http://store.company.com/dir/other.html 执行以下语句document.domain = "company.com";
这条语句执行之后,页面将会成功地通过对 http://company.com/dir/page.html 的同源检测。同理,compan.com
不能设置 document.domain
为 othercompany.com
浏览器单独保存端口号。任何的赋值操作,包括 document.domain = document.domian
都会以 null
值覆盖掉原来的端口号,因此 company.com:8080
页面的脚本不能仅通过设置 document.domain = "company.com"
就是与 company.com
通信。赋值是必须带上端口号,以确保端口号不会为 null
注意: 使用document.domain允许子域安全访问其父域时,需要设置 document.domain 在父域和子域中具有相同的值,虽然这样做只是将父域设置回其原始值,不做的话可能会导致权限错误
跨源网络访问
同源策略控制了不同源之间的交互,例如在使用XMLHTTPRequest或标签时则会收到同源策略的约束,交互通常分为三类:
- 通常允许进行跨域操作(Cross-orgin writers) 例如:链接(links),重定向以及表单提交,特定少数的HTTP请求需要添加preflight
- 通常允许跨域资源嵌入(Cross-orgin embedding)
- 通常不允许跨域读操作(Cross-orgin reads)。但常可以通过内嵌资源来巧妙的进行读取访问 例如可以读取图片的高度和宽度,调用内嵌脚本的方法
下面是可能嵌入跨源的资源的一些示例:
<script src="..."></script>
标签嵌入跨域脚本。语法错误信息只能在同源脚本中捕捉到<link rel="stylesheet" href="...">
标签嵌入css,由于css的,css的跨域需要一个设置正确的Content-Type消息头,不同浏览器有不同的限制<img>
嵌入图片。支持的图片格式包括PNG,JPEG,GIF,BMP,SVG<video>
和<audio>
嵌入多媒体资源<object>
,<embed>
和<applet>
的插件@font-face
引入的字体,一些浏览器允许跨域字体(cross-origin fonts),一些需要同源字体(same-origin fonts)<frame>
和<iframe>
载入的任何资源,站点可以使用X-Frame-Options消息头来阻止这种形式的跨域交互
如何允许跨域访问
使用CORS允许跨源访问
如何阻止跨源访问
- 阻止跨域写操作,只要检测请求中的一个不可测的标记(CSRF token)即可,这个标记被称为Cross-Site Request Forgery (CSRF) 标记。必须使用这个标记来阻止页面的跨站读操作
- 阻止资源的跨站读取,需要保证该资源是不可嵌入的。阻止嵌入行为是必须的,因为嵌入资源通常向其暴露信息
- 阻止跨站嵌入,确保你得资源不能是以上列出的可嵌入资源格式。多数情况下浏览器都不会遵守Conten-Type消息头。例如,如果你在
<script>
标签中嵌入HTML文档,浏览器仍将HTML解析为Javascrip
跨源脚本API访问
Javascript的APIs中,如 iframe.contentWindow
,window.parent
, window.open
和 window.opener
允许文档间直接相互引用。当两个文档的源不同时,这些引用方式将对 Window 和 Location对象的访问添加限制。可以使用window.postMessage
作为替代方案,提供跨域文档间的通讯
窗口:
允许一下对窗口属性的跨源访问
Methods:
window.blur
window.close
window.focus
window.postMessage
Attributes
window.closed 只读
window.frames 只读
window.length 只读
window.location 读/写
window.opener 只读
window.parent 只读
window.self 只读
window.top 只读
window.window 只读
某些浏览器允许访问比规范允许的更多属性
位置
允许一下对位置属性的跨源访问:
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都是使用不安全的连接设置的