当前位置: 首页 > 科技观察

为什么CORS可以保证安全?为什么只对复杂的请求进行预检?_0

时间:2023-03-12 04:37:32 科技观察

大家好,我是念念!说到CORS,大部分文章都是在讲什么是简单请求,什么是复杂请求,什么是复杂请求预校验的过程。但是如果我问你:CORS为什么要带源,这是为了保证当前站点的安全还是目的服务器的安全?为什么要区分简单请求和复杂请求,只预检复杂请求?本文将围绕CORS是如何保证安全的,厘清这些问题。看完你就知道CORS是什么,为什么。什么是CORS相信每个前端控制台都打印过这样一段话,告诉你:你的跨域请求策略被拦截了!首先要明确一点,CORS的目的不是为了拦截请求,而是为了让它们能够正常请求。CORS诞生的背景是“同源策略”。这是一个相当严格的规则,它禁止跨源AJAX请求。但是在实际开发中有这样的需求,所以开了一个口子——只要配置好CORS的相应规则,跨域请求就可以正常进行了。这也对应了CORS的名称——“跨域资源共享”,就是在“同源策略”的背景下,允许进行跨域请求。回到上面提到的控制台报错,这不是阻止你跨域请求,而是提醒你:因为你没有按照CORS要求进行配置,所以暂时屏蔽掉。上面已经解释了如何配置CORS。只要按照CORS要求进行配置,就可以突破同源策略的限制。下面将介绍如何配置。这部分不需要前端操心,完全由后端来完成:在响应头中添加一个字段Access-Control-Allow-Origin(允许请求的来源),这个值应该包括前端的来源。例如:请求的后端接口是http://fe_nian,而你在本地开发一个前端项目,运行在8080端口,那么后端会在响应头中添加Access-Control-Allow-Origin:*来允许originhttp://localhost:8080进行跨域请求,因为*代表一切。跨域请求的过程CORS将请求分为简单请求和复杂请求,划分的依据是“是否会产生副作用”。简单贴上定义,同时满足下面两个条件的简单请求方式就是HEAD/GET/POST。请求体的文件类型只能是form-urlencoded,form-data,text/plain(这样的文章很多,不再赘述,可以看阮一峰-跨域资源分享)。对于一个简单的请求,流程是这样的:浏览器发起请求,自动将请求的来源添加到服务器进行校验。服务器返回数据,返回校验结果,配置CORS响应头。浏览器检查CORS响应头,如果包含当前源则允许,否则阻塞。这里需要注意的是,浏览器拦截的是响应,而不是请求。跨域请求发出去,服务器响应,但是浏览器拦截了。对于复杂的请求,整个过程是这样的:浏览器发起一个带有请求来源的预检请求,不包含请求体。服务器返回校验结果并配置CORS头。浏览器发出真正的请求。浏览器返回数据。浏览器会检查第2步得到的CORS头,如果不包含当前源,则不会执行后面的第3步和第4步,即不会发起真正的请求。为什么带源码CORS在给开发带来方便的同时,也带来了安全隐患——CSRF攻击。其基本流程如下:用户登录受害网站,将获得的身份凭证保存在浏览器的cookie中。即上图的①②③。用户用同一浏览器打开黑客网站,黑客网站向受害网站服务器发送恶意请求。这时浏览器会自动从cookie中取出身份证明并带上。即上图中的④⑤。受害网站服务器发现有身份证明,成功接受恶意请求。如果严格遵循同源策略,则无法进行第2步的跨域请求,也不会造成危害。因此,CORS策略的心智模型是:所有的跨域请求都是不安全的,浏览器必须将源码带到服务器端进行检查。如果做过服务器端开发的应该知道,服务器端是不存在跨域的,从另外一台服务器获取资源是非常顺利的。因为服务器不像浏览器,将用户凭证存储为“容器”——也就是上面第一步发生的事情,所以它在进行跨域请求时不存在这种风险。为什么只对复杂请求做preflight上面说过,简单请求和复杂请求的划分依据是“是否产生副作用”。这里的副作用指的是对数据库进行修改:使用GET请求获取新闻列表,数据库中的记录不会发生变化,而使用PUT请求修改记录时,数据库中的记录会发生变化。对于简单的请求,浏览器只会在请求头中增加一个origin字段来标识请求的来源;对于非简单的请求,浏览器会先发送预检请求,直到得到肯定的答复才会发送真正的请求,下面会解释为什么要这样做。可以假设网站被CSRF攻击——黑客网站向银行服务器发起跨域请求,银行安全意识薄弱,只要有登录凭证cookie就可以成功响应:黑客网站发起GET请求,查看受害用户本月的账单。银行的服务器会返回正确的数据,但是影响不大,而且由于浏览器的拦截,黑客最终没有拿到数据;黑客网站发起PUT请求清空受害用户的账户余额。浏览器会先做预检,发现收到的响应没有携带CORS响应头,所以不会发送真正的PUT请求;幸好有预校验机制,否则一旦发送PUT请求,黑客的攻击就得逞了。结论回到开头的两个问题,不难得出答案:对于跨域请求,请求的来源是为了防止CSRF攻击;浏览器的心智模型是:跨域请求是不安全的,CORS机制是为了保证请求目的服务器的安全。根据对服务器是否有副作用,分为简单请求和复杂请求(但由于历史原因,表单POST请求也分为简单请求),预检查机制会拦截不安全的复杂请求,避免危害到服务器,而简单的请求通常不会修改服务器的资源,即使发出也没有什么危害。