什么是同源策略?

一、定义

同源策略(same origin policy)是netScape(网景)提出的一个安全策略,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,浏览器很容易受到XSS、CSFR等攻击。具体表现为浏览器在执行脚本前,会判断脚本是否与打开的网页是同源的,判断协议、域名、端口是否都相同,相同则表示同源。其中一项不相同就表示跨域访问。会在控制台报一个CORS异常,目的是为了保护本地数据不被JavaScript代码获取回来的数据污染,因此拦截的是客户端发出的请求回来的数据接收,即请求发送了,服务器响应了,但是无法被浏览器接收。

浏览器采用同源策略,在没有明确授权的情况下,禁止页面加载或执行与自身不同源的任何脚本。

二、同源策略分类

  1. DOM 同源策略: 禁止对不同源页面 DOM 进行操作。这里主要场景是 iframe 跨域的情况,不同域名的 iframe 是限制互相访问的(比如一个恶意网站的页面通过iframe嵌入了银行的登录页面(二者不同源),如果没有同源限制,恶意网页上的javascript脚本就可以在用户登录银行的时候获取用户名和密码)
  2. XMLHttpRequest 同源策略: 禁止使用 XHR 对象向不同源的服务器地址发起 HTTP 请求(这一点里面其实包括了 ajax)。
  3. Cookie、LocalStorage、IndexedDB 等存储性内容同源策略: js中无法访问不属于同个源的cookie、LocalStorage中存储的内容。

具体来说,cookie和LocalStorage在控制哪些源可以访问的问题上还是细微的差别,父域在设置cookie的时候可以设定允许子域访问这段cookie,同时Cookie只和域名以及路径关联,如果是同个域名不同端口的源依然是共享同个域名下的Cookie的,而LocalStorage则是以源为单位进行管理,相互独立,不同源之间无法相互访问LocalStorage中的内容。

但是同源策略留了一些“后门”:

  1. 页面中的链接,重定向以及表单提交是不会受到同源策略限制的(未授权情况下,ajax 的表单提交是不被允许的,但是普通的表单是可以直接跨域的)。
  2. <script>、<img>、<link>这些包含 src 属性的标签可以加载跨域资源。但浏览器限制了JavaScript的权限使其不能读、写加载的内容。

总结:

同源策略限制的内容有:

  • Cookie、LocalStorage、IndexedDB 等存储性内容
  • DOM和JS对象无法获得
  • AJAX请求发送后,结果被浏览器拦截了

但是有三个标签是允许跨域加载资源:

  • <img src=XXX>
  • <link href=XXX>
  • <script src=XXX>

三、同源策略的目的

简单的举两个例子

(1)如果没有 DOM 同源策略,也就是说不同域的 iframe 之间可以相互访问,那么黑客可以这样进行攻击:

  1. 做一个假网站,里面用 iframe 嵌套一个银行网站mybank.com
  2. 把 iframe 宽高啥的调整到页面全部,这样用户进来除了域名,别的部分和银行的网站没有任何差别。
  3. 这时如果用户输入账号密码,我们的主网站可以跨域访问到mybank.com 的 dom 节点,就可以拿到用户的账户密码了。

(2)如果 XMLHttpRequest 同源策略,那么黑客可以进行 CSRF(跨站请求伪造) 攻击:

  1. 用户登录了自己的银行页面mybank.com,银行页面向用户的cookie 中添加用户标识。
  2. 用户浏览了恶意页面evil.com,执行了页面中的恶意AJAX 请求代码。
  3. evil.com向mybank.com发起 Ajax HTTP 请求,浏览器会默认把mybank.com对应 cookie 也同时发送过去。
  4. 银行页面从发送的 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,因为请求毕竟是发出去了。

跨域访问限制的作用流程大致如下:

  1. 浏览器发送跨域请求
  2. 接收response数据
  3. 检查响应头
    (1)如果响应头中没有允许跨域访问的配置,则不加载,并报出响应异常
    (2)如果响应头中有允许跨域访问的设置,正常加载数据

即: 同源策略并不是浏览器不让请求发出去、或者后端拒绝返回数据。实际情况是请求正常发出去了,后端也正常相应了,只不过数据到了浏览器后浏览器不去作用加载而是丢弃了。

小结:
同源策略控制不同源之间的交互,这些交互通常分为三类:
(1)跨域读: 同源策略是不允许这种事情发生的,如果可以你就能使用 js 读取嵌入在 iframe 中的页面的 dom 元素,获取敏感信息了

(2)跨域写: 同源策略不阻止这种操作,比如向不同源的地址发送 POST 请求等,但是这种允许只限制在普通表单(而且是在没有 CSRF token 或者验证 referer 的情况下),对于 ajax 这种方式也是默认不允许的,如果随随便便允许就会出现使用 ajax 来进行 CSRF 请求的情况了

(3)跨域嵌入: 这种方式是默认允许的,我们可以在一个源中通过 iframe 嵌入 另一个源的页面,但是如果想限制这种操作的话,我们可以设置 x-frame-options 这个头,这样设置了这个头的页面就允许被嵌入到不同源的页面中了

文章作者: GeYu
文章链接: https://nuistgy.github.io/2023/01/07/同源策略/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Yu's Blog