一、定义
同源策略(same origin policy)是netScape(网景)提出的一个安全策略,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,浏览器很容易受到XSS、CSFR等攻击。具体表现为浏览器在执行脚本前,会判断脚本是否与打开的网页是同源的,判断协议、域名、端口是否都相同,相同则表示同源。其中一项不相同就表示跨域访问。会在控制台报一个CORS异常,目的是为了保护本地数据不被JavaScript代码获取回来的数据污染,因此拦截的是客户端发出的请求回来的数据接收,即请求发送了,服务器响应了,但是无法被浏览器接收。
浏览器采用同源策略,在没有明确授权的情况下,禁止页面加载或执行与自身不同源的任何脚本。
二、同源策略分类
- DOM 同源策略: 禁止对不同源页面 DOM 进行操作。这里主要场景是 iframe 跨域的情况,不同域名的 iframe 是限制互相访问的(比如一个恶意网站的页面通过iframe嵌入了银行的登录页面(二者不同源),如果没有同源限制,恶意网页上的javascript脚本就可以在用户登录银行的时候获取用户名和密码)
- XMLHttpRequest 同源策略: 禁止使用 XHR 对象向不同源的服务器地址发起 HTTP 请求(这一点里面其实包括了 ajax)。
- Cookie、LocalStorage、IndexedDB 等存储性内容同源策略: js中无法访问不属于同个源的cookie、LocalStorage中存储的内容。
具体来说,cookie和LocalStorage在控制哪些源可以访问的问题上还是细微的差别,父域在设置cookie的时候可以设定允许子域访问这段cookie,同时Cookie只和域名以及路径关联,如果是同个域名不同端口的源依然是共享同个域名下的Cookie的,而LocalStorage则是以源为单位进行管理,相互独立,不同源之间无法相互访问LocalStorage中的内容。
但是同源策略留了一些“后门”:
- 页面中的链接,重定向以及表单提交是不会受到同源策略限制的(未授权情况下,ajax 的表单提交是不被允许的,但是普通的表单是可以直接跨域的)。
<script>、<img>、<link>
这些包含 src 属性的标签可以加载跨域资源。但浏览器限制了JavaScript的权限使其不能读、写加载的内容。
总结:
同源策略限制的内容有:
- Cookie、LocalStorage、IndexedDB 等存储性内容
- DOM和JS对象无法获得
- AJAX请求发送后,结果被浏览器拦截了
但是有三个标签是允许跨域加载资源:
<img src=XXX>
<link href=XXX>
<script src=XXX>
三、同源策略的目的
简单的举两个例子
(1)如果没有 DOM 同源策略,也就是说不同域的 iframe 之间可以相互访问,那么黑客可以这样进行攻击:
- 做一个假网站,里面用 iframe 嵌套一个银行网站mybank.com
- 把 iframe 宽高啥的调整到页面全部,这样用户进来除了域名,别的部分和银行的网站没有任何差别。
- 这时如果用户输入账号密码,我们的主网站可以跨域访问到mybank.com 的 dom 节点,就可以拿到用户的账户密码了。
(2)如果 XMLHttpRequest 同源策略,那么黑客可以进行 CSRF(跨站请求伪造) 攻击:
- 用户登录了自己的银行页面mybank.com,银行页面向用户的cookie 中添加用户标识。
- 用户浏览了恶意页面evil.com,执行了页面中的恶意AJAX 请求代码。
- evil.com向mybank.com发起 Ajax HTTP 请求,浏览器会默认把mybank.com对应 cookie 也同时发送过去。
- 银行页面从发送的 cookie 中提取用户标识,验证用户无误,response中返回请求数据。此时数据就泄露了,而且由于 Ajax 在后台执行,用户无法感知这一过程。
注:
- cookie 是浏览器根据你请求的页面进行发送的,而与从哪个页面发送过去请求无关。比如说,我访问了 a.com 以后获取了 a.com 的cookie ,然后我访问 q.com ,由于 cookie 域的限制,浏览器在请求 q.com 的时候不会携带 a.com 的cookie ,但是当 q.com 有一个向 a.com 发起的请求的时候,浏览器就会自动的在这个请求中带上 a.com 的 cookie。
- 跨域并不是请求发不出去,请求能发出去,服务端能收到请求并正常返回结果,只是结果被浏览器拦截了。
你可能会疑问明明通过表单的方式可以发起跨域请求,为什么 Ajax 就不会?
因为归根结底,跨域是为了阻止用户读取到另一个域名下的内容,Ajax 可以获取响应,浏览器认为这不安全,所以拦截了响应。但是表单并不会获取新的内容,所以可以发起跨域请求。同时也说明了跨域并不能完全阻止 CSRF,因为请求毕竟是发出去了。
跨域访问限制的作用流程大致如下:
- 浏览器发送跨域请求
- 接收response数据
- 检查响应头
(1)如果响应头中没有允许跨域访问的配置,则不加载,并报出响应异常
(2)如果响应头中有允许跨域访问的设置,正常加载数据
即: 同源策略并不是浏览器不让请求发出去、或者后端拒绝返回数据。实际情况是请求正常发出去了,后端也正常相应了,只不过数据到了浏览器后浏览器不去作用加载而是丢弃了。
小结:
同源策略控制不同源之间的交互,这些交互通常分为三类:
(1)跨域读: 同源策略是不允许这种事情发生的,如果可以你就能使用 js 读取嵌入在 iframe 中的页面的 dom 元素,获取敏感信息了
(2)跨域写: 同源策略不阻止这种操作,比如向不同源的地址发送 POST 请求等,但是这种允许只限制在普通表单(而且是在没有 CSRF token 或者验证 referer 的情况下),对于 ajax 这种方式也是默认不允许的,如果随随便便允许就会出现使用 ajax 来进行 CSRF 请求的情况了
(3)跨域嵌入: 这种方式是默认允许的,我们可以在一个源中通过 iframe 嵌入 另一个源的页面,但是如果想限制这种操作的话,我们可以设置 x-frame-options 这个头,这样设置了这个头的页面就允许被嵌入到不同源的页面中了