背景
在聊正文之前,我们先来了解一下web的发展史,大概了解一下web安全的演变历程。
1989年,蒂姆·伯纳斯-李爵士设计发明了第一个浏览器,架设了第一个 web 服务器 info.cern.ch。那时候的浏览器就是非常简单的理念,就是知识共享,输入正确的网址你就能获取到相应的内容。
随着时代发展,浏览器的功能也越来越强大,从最开始的只能阅读文档,到后面可以查看图片,音频等,互联网不再只是用于简单的浏览文档信息、查看门户网站这些。
这时候,交互式web开始兴起,如用户登录,购买商品,各种论坛等,于是乎人们发现了一些问题,客户端和服务器的传输使用的是http协议,http协议是无状态的,那么服务器就不知道这一次请求的人,跟之前登录请求成功的人是不是同一个人
由于http协议的无状态,服务器忘记了之前的所有请求,它无法确定这一次请求的客户端,就是之前登录成功的那个客户端。
于是cookie出现了
Cookie
Cookie是由服务器发给客户端的特殊信息,而这些信息以文本文件的方式存放在客户端,然后客户端每次向服务器发送请求的时候都会带上这些特殊的信息,用于服务器记录客户端的状态。
cookie在行使自身使命的同时,也存在了安全隐患。
设想一下,你登录了银行系统,并把登录信息、账号密码存在了cookie中,cookie信息共享,大家的账号和密码也共享了,奋斗了一辈子的财富,被别人轻易的转走,将是多么可怕的事情。所以,为了维护互联网的隐私和数据安全,必须制定一定规则。这就是同源策略
同源策略
同源策略的概念要追溯到1995年的网景浏览器。作为一个重要的安全基石,所有的现代浏览器都在一定程度上实现了同源策略。
在这个策略下,web浏览器允许第一个页面的脚本访问第二个页面里的数据,但是也只有在两个页面有相同的源时。
而所谓的源是由协议,主机名,端口号组合而成的。这个策略可以阻止一个页面上的恶意脚本通过页面的DOM对象获得访问另一个页面上敏感信息的权限,同一个内的脚本只能读写本域内的资源,**对于其它域的资源,它没有禁止脚本的执行,而是禁止读取HTTP回复,**这种安全限制称为同源策略。
限制范围
无法读取非同源网页的 Cookie、LocalStorage 和 IndexedDB。
无法接触非同源网页的 DOM。
无法向非同源地址发送 AJAX 请求(可以发送,但浏览器会拒绝接受响应)
同源策略提升了Web前端的安全性,但阻碍了Web拓展的灵活性。**若把html、js、css、flash,image等静态文件全部布置在一台服务器上,会加大这台服务器的压力,甚至威胁到web服务的可用性。**因此,在遵循同源策略的基础上,在安全性和可用性之间选择了一个平衡点。以下这些标签不受同源策略的影响
* <img src=XXX>
* <link href=XXX>
* <script src=XXX>
* <iframe>
虽然有上述对同源策略的补充,但是有时候我们不得不需要访问其它域的资源(例如服务器集群)所以就有了跨域的解决方案
跨域解决方案
首先我们需要知道跨域浏览器的同源策略下的产物,所以服务端不存在跨域的问题,服务器间的调用,一般是为了获取接口数据的,功能单一。app也相对比较自由,可自行设置白名单等限定条件。
1、通过jsonp跨域(get请求)
JSONP 是 JSON with Padding 的略称。它是一个非官方的协议,利用<script> 标签没有被同源策略限制,向网页中动态插入script标签,向服务端发送请求,并允许用户传递一个callback参数给服务端,然后服务端返回数据时会将这个callback参数作为函数名来包裹住JSON数据,这样前端可以随意定制自己的函数来自动处理返回数据了。
显而易见,JSONP并不是一个好的跨域解决方案,它至少有着下面两个严重问题:
- 它需要服务器端支持,但在非跨域的情况下,服务器又需要响应一个正常的JSON格式
- 只能发送 get 请求,
script元素发出的请求只能是get请求
2、CORS跨域
CORS是基于http1.1的一种跨域解决方案,它的全称是Cross-Origin Resource Sharing,跨域资源共享。它也是目前应用最为广泛的一种解决方案。
它的总体思路是:如果浏览器要跨域访问服务器的资源,需要获得服务器的允许
而要知道,一个请求可以附带很多信息,从而会对服务器造成不同程度的影响,比如有的请求只是获取一些新闻,有的请求会改动服务器的数据,针对不同的请求,CORS规定了三种不同的交互模式,分别是:
- 简单请求
请求方法是 get post head
请求只包含这些字段 Accept、Accept-Language、Content-Language、Content-Type、DPR、Downlink、Save-Data、Viewport-Width、Width
请求头content-type只包含 text/plaiin、multipart/form-data、application/x-www-form-urlencoded
-
需要预检查的请求
-
附带身份证的请求
当浏览器判定某个ajax跨域请求是简单请求时,会发生以下的事情
-
请求头中会自动添加
Origin字段 -
服务器响应头中应包含
Access-Control-Allow-Origin
判定为预检查请求
-
浏览器发送预检请求,询问服务器是否允许
OPTIONS /api/user HTTP/1.1 Host: crossdomain.com ... Origin: my.com Access-Control-Request-Method: POST Access-Control-Request-Headers: a, b, content-type
2. 服务器允许
HTTP/1.1 200 OK
Date: Tue, 21 Apr 2020 08:03:35 GMT
...
Access-Control-Allow-Origin: http://my.com
Access-Control-Allow-Methods: POST
Access-Control-Allow-Headers: a, b, content-type
Access-Control-Max-Age: 86400
...
3.浏览器发送真实请求
- 服务器完成真实的响应
判定为附带身份查请求
// xhr
var xhr = new XMLHttpRequest();
xhr.withCredentials = true;
// fetch api
fetch(url, {
credentials: "include"
})
当一个请求需要附带cookie时,无论它是简单请求,还是预检请求,都会在请求头中添加cookie字段
而服务器响应时,需要明确告知客户端:服务器允许这样的凭据
告知的方式也非常的简单,只需要在响应头中添加:Access-Control-Allow-Credentials: true即可
对于一个附带身份凭证的请求,若服务器没有明确告知,浏览器仍然视为跨域被拒绝。
另外要特别注意的是:对于附带身份凭证的请求,服务器不得设置 Access-Control-Allow-Origin 的值为*