聊聊CORS的过度设计缺陷

注:本文不讲CORS原理,且默认你已了解CORS,但对其中一些细节抱有同样的怀疑

同源策略

浏览器的同源策略自其诞生以来就一直存在,如何进行跨域Hack,一代又一代的前后端都为此费尽了心机。

首先可以肯定的是,同源策略的限制是合理的。因为,服务端后台接口就部署在那,任何人只要构造了一个正确的HTTP请求,都能得到服务器的应答。但很显然,我们不希望我们的接口任何人都能访问(事实上,因为大家采用的都是同一套HTTP标准,所以你永远也无法杜绝这件事的发生)。还有一个更直接的理由,越多人知道你的接口,你的接口就越危险。

但是既然存在跨域的普遍呼声,那么这个需求也是合理的。所以一个新的跨域规范诞生了,再也不需要丑陋的JSONP,更不需要域内代理浪费资源。这个规范叫,CORS(Cross-Origin-Resource-Sharing)-跨域资源共享。CORS需要服务端与客户端同时支持,客户端就是指浏览器,目前所有浏览器最新版本都已支持,如下图所示:

聊聊CORS的过度设计缺陷_第1张图片

服务端的支持,常规做法是,在代码中用全局拦截器做处理。

 

一个潜伏了数十年的毒瘤

在讲服务器的支持逻辑之前,先讲个扩展话题。浏览器限制跨域请求,实际上有两种方式:
---请求正常发起,拦截返回数据
---在请求发起前拦截住

事实上,绝大多数浏览器都是采用第一种方式。看到这里我倒吸一口凉气,那也就是说,你的请求成功了,数据库也产生了修改,但是浏览器却告诉你,你的请求被禁止了。这就好比,张三强奸了隔壁老王的女朋友,有一个警察也在现场,但是他却等张三强奸完了才告诉他,他的行为是违法犯罪。

天呐,这是多么愚蠢的一个设计,而且竟然几乎所有浏览器都是这么干的,一干就是十几年。我不否认他们这么做可能出于一些不得已的苦衷。所以这次的CORS规范就是为了填补这个漏洞,主要手段是使用预检请求。这就好比,在张三脱裤子之前,警察先问了女方是否愿意。

 

CORS问题点

我们知道,所谓预检请求,都是通过OPTIONS请求方法发出的。需要注意的是,预检请求不会携带任何的请求参数,所以你不需要担心数据库会被修改两次。服务器需要做的就是,在全局拦截器中,设置Access-Control-Allow系列跨域头。

问题就出在这个跨域头这里。需要设置的跨域头主要有三个:
Access-Control-Allow-Origin: 允许访问的域名
Access-Control-Allow-Methods: 允许使用的请求方法
Access-Control-Allow-Headers: 允许使用的请求头

第一个,没问题,很合理。因为我确实要限制非法网站过来的请求
第二个,也没毛病。因为确实存在,某些controller只支持特定的HTTP方法。Restful接口中这种限制体现得尤为明显。

问题出在第三个
第三个,就特么非常皮了。这个是用来告诉前端,我允许你接下来的请求附带哪些请求头。或许换个说法更容易暴露它的不合理,就是,我服务器支持哪些请求头。需要注意的是,这里必须把所有支持的请求头都填上。天呐,要知道,所有的HTTP请求头,基本的,常见的,不常见的,加起来有几十个呢。如果服务器要全部支持,就要把所有的请求头都填进去。这将是非常枯燥,而且不必要的工作。

增加了代码量是一方面,还有另一个更充分的理由来反驳它。让我们用服务器解析请求头的伪代码来说明一下。

if(支持这个请求头的解析){
  if(请求带这个请求头){
    解析请求头
  }else{
    忽略
  }
}else{
  if(请求带这个请求头){
    忽略
  }else{
    忽略
  }
}

服务器的解析逻辑是,如果我不支持这个请求头,你传与不传,我都会忽略掉,不会对我服务器构成任何安全威胁。这样一来,他所谓的为了安全跨域的说法也根本站不住脚,因为威胁本身就不存在。
这样做,我唯一能想到的一个好处是,大概能节省几个字节的传输数据,避免网络带宽的浪费。真的是用心良苦啊,为了提高宅男屌丝们看小视频的用户体验,为了帮广大网民每个月节省几分钱的手机流量,这个协议的制定者是操碎了心啊。

只能说这个规范的制定者考虑太细,太周到了,就像一个无微不至的慈母,生怕他已经成年的孩子磕着碰着,走路都要牵着他的手。这就在很大程度上限制了开发者的自由,无端给他们增加了更多负担。

你可能感兴趣的:(日常抓虾)