什么是浏览器的同源策略?
2023-02-06 11:29:20
## 一、定义
**同源策略(same origin policy)** 是一个安全策略,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,浏览器很容易受到 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. 用户浏览了恶意页面 `xxx.com`,执行了页面中的恶意Ajax 请求代码。
3. `xxx.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)跨域读:** 同源策略是不允许这种事情发生的,如果可以你就能使用 js 读取嵌入在 iframe 中的页面的 dom 元素,获取敏感信息了
**(2)跨域写:** 同源策略不阻止这种操作,比如向不同源的地址发送 POST 请求等,但是这种允许只限制在普通表单(而且是在没有 CSRF token 或者验证 referer 的情况下),对于 ajax 这种方式也是默认不允许的,如果随随便便允许就会出现使用 ajax 来进行 CSRF 请求的情况了
**(3)跨域嵌入:** 这种方式是默认允许的,我们可以在一个源中通过 iframe 嵌入 另一个源的页面,但是如果想限制这种操作的话,我们可以设置 x-frame-options 这个头,这样设置了这个头的页面就允许被嵌入到不同源的页面中了
## 扩展阅读
[什么是跨域资源共享(CORS)?](https://www.daimaku.net/post/view/23090)