关于跨域与 csrf 的那些小事

6,536 阅读8分钟

前言

  在这段时间,部门前辈分享了不少干货。我学到了不少内容,并对其进行简单整理,以便更好地转化为自己的知识。

知识探讨部分

关于跨域

产生

  1. 协议,域名,端口三者其中存在不同都会形成跨域;故,当协议,端口,域名三者均相同时,浏览器就会认为是同源,允许加载该资源,否则为不同源。
  2. 跨域存在原因:浏览器的同源限制策略
  • 请求:客户端(www.a.com) -》 服务端(www.baidu.com)
  • 响应:客户端(www.a.com)《- 服务端(www.baidu.com)

  从上面列出的例子中,我们可以看到客户端和服务端是处于不同的域名下,这种情况,客户端可以正常地向服务端发出请求。但是,由于浏览器的同源限制策略,服务端响应的数据会被浏览器过滤掉,并抛出常见的跨域报错。

网上随便找了一张常见的跨域报错的图

特别需要提一下的是:IE 与其他浏览器对于同源策略的处理方式稍有点不同。主要包括以下两点:

  • 授信范围:两个相互之间高度互信的域名,不受同源策略的限制。
  • 端口:IE 未将端口号加入到同源策略的组成成分中。即两个协议,域名相同,端口不同的域名,IE 会认为两者是属于同源并不受限制的。

为什么会被过滤?

  浏览器收到响应数据之后,会判断响应回数据的源和当前页面的源是否是属于同源。针对不同源,如果后端没有对响应字段进行处理,则响应回的数据会被浏览器直接过滤掉。

发生跨域时,允许进行的操作

  1. 通常允许跨域写操作(link、redirect、表单提交)
  2. 通常允许跨域资源嵌入(script、img、video...)
  3. 通常禁止跨域读操作(ajax)
  4. 可以正常发送请求,可以携带Cookie(withCredentials),但是浏览器会限制来自于不同域的资源的接收

跨域限制的资源

  • 1.数据存储限制:Cookie, LocalStorage, IndexDB 无法读取
  • 2.脚本 API 限制:DOM 无法操作
  • 3.网络请求限制:XHR 请求无法接收响应

第1点:

(1) LocalStorage、IndexedDB 以源进行分割,每个源都拥有自己单独的存储空间,一个源中的 JavaScript 脚本不能对其它源的数据进行读写操作。

(2)cookie 的同源限制只要体现在域方面,与端口,协议无关。用户可以获取和设置当前域以及当前域对应父域下的 cookie。

第2点:

(1)脚本API限制:浏览器限制了 js 的跨站点(不同源)调用或操作。如:来源于 A 源页面的脚本,只能操作“同源”的页面,而来源于 B 源页面的脚本不能操作 A 源页面的 DOM。

(2)当然,需要特别提醒一下的是,对于一段 js 脚本来说,该脚本的“源”指的是加载该脚本页面的源,与存储该脚本的地址无关。如:A(https://www.a.com)页面下加载了两段脚本,分别为:b.js (源于http://www.b.com)和c.js(https:www.c.com)。b.js 和 c.js 被加载至A页面下,与 A 同源,所以 b 和 c 脚本都可以操作 A 页面的 DOM。

  进一步思考,我们会发现 A 页面地址与 b 和 c 脚本存储地址都处于不同源下,按道理,应该会出现跨域限制(同源限制),但结果是没有。其实这个是取决于浏览器的一个特性,浏览器的同源策略不对src属性做限制,也就是说像<script>,<img>,<link>等这些标签是可以从不同源的网站中获取到资源的。

解决方式

  跨域的解决方案有很多( jsonp,反向代理,使用 document.domain 设置为同域,跨域资源共享等等),这里就不详细说了,但特别想提一下是跨域资源共享(cors)

cors 主要是进行以下处理:

  服务端设置响应头的 access-control-allow-origin 的值为允许请求的域(客服端的)或设置为*(即匹配任意域名,任意客户端都可访问);即可获取到该服务端响应的跨域资源。

原因:

  当响应数据从服务端回到浏览器时,浏览器得知客户端和服务端属于不同的域,但同时浏览器又看到响应数据的响应头中 access-control-allow-origin 的字段值是包含当前客户端地址或*,浏览器则不会对响应的数据进行过滤。

拓展知识点:web 安全之 csrf(跨站伪造请求)

攻击成功的操作步骤如下:

csrf 特征:
  • 攻击⼀般来源于第三方域名
  • ccsrf 不能获取到 cookie,但是可以利用浏览器的特性去使用。
  • 接口的所有参数都是可以预测的(攻击网站清楚要伪造请求接口的请求参数)

  从前面提到的跨域知识点中,能了解到浏览器对于 cookie 也是存在同源限制的,也就是与 cookie(domain)处于不同源的网站,浏览器是不会让该网站获取到这个 cookie。那为什么csrf攻击还会成功呢?其实这个与浏览器使用 cookie 的方式有关。

浏览器使用 cookie 情况主要包括以下几点:

  1. 除了跨域 XHR 请求情况下,浏览器在发起请求的时候会把符合要求的 cookie 自动带上。(域名,有效期,路径,secure 属性)

  2. 跨域 XHR 的请求的情况下,也可以携带 Cookie。

  3. 浏览器允许跨域提交表单

  也就是说,浏览器中有页面或网站向某个域名发送请求时,其请求都会自动带上该域名下的所有 cookie。

csrf 防御途径

简单列出三点

1. Referer: HTTP请求完整来源路径(不完善)

针对通过 Referer 字段去判断请求来源是否合法是不太靠谱的,这个字段很有可能会丢失:

1.IE6、7下使用 window.location.href=url 进行界面的跳转或者 window.open,都会造成 Referer 丢失。

2.HTTPS 页面跳转到 HTTP 页面,所有浏览器 Referer 都丢失。

3.点击 Flash 上到达另外一个网站的时候,Referer 的情况就比较杂乱,不太可信。

可以附加使用于判断请求来源,但是仅通过该字段进行判断是不太靠谱的,如果请求头丢失该字段,则服务端无法判断当前请求来源,无法校验请求是否合法。

2. Origin: CORS 中的请求头,当跨域访问时,会携带此请求头(不完善)

在以下情况不存在此请求头:

302 重定向: 
1. 在 302 重定向之后 Origin 不包含在重定向的请求中,因为 Origin 可能会被认为是其他来源的敏感信息。
2.对于 302 重定向的情况来说都是定向到新的服务器上的 URL,因此浏览器不想将 Origin 泄漏到新的服务器上。

Origin 和 Referer 存在相同的问题,同样有可能丢失该字段。

3. token 校验(常用方式)

最常用的一种是通过token去校验请求是否合法:

校验原理:
  1. 后端生成 token,并存在 session 中。
  2. 用户请求成功后,后端将 token 发送到客户端,发送方式主要是为以下两种:

(1)服务端将 token 渲染到 html 中。 也就是通过一个 dom 结点保存 token 信息,客户端就可以通过 dom 操作获取到该 token 内容。(同源策略会限制脚本 API 操作)

(2)服务端将 token 设置到 cookie 中。 客户端从 cookie 中获取(同源策略限制 cookie 操作) 3. 客户端在获取到 token 后,在下一次进行比较关键的请求操作时,将 token 发送到服务端。

发送 token 到服务端的方式主要包括两种:

  • 在请求头中将获取到的 token 设置到 cookie 中。
  • 将 token 放到请求参数中。
  1. 服务端在接收到请求后,会从请求头中取出 token,并和 session 中的 token 进行比较,一致则表示身份验证通过,再返回相应的信息;否则,则校验不通过。

补充:

  token 校验之所以能防御 csrf,是因为相信浏览器的同源策略。为什么这么说?因为只有在同源的情况下,页面才能进行脚本操作和使用 js 获取 cookie 的操作,才能获取到 token。也就是说第三方网站是没有办法拿到 token 的。只有真正有权限的网站或页面才有办法取到 token,并将 token 传到服务端。所以服务端默认带有相应 token 的请求都是合法的请求。

注:设置 http only,禁止通过 js 操作 cookie

参考文章