起因

其实上半年就已经和他打过交道了,只是当时误打误撞成功配置后就再也没有管过,未曾想最近又一次和它纠缠上,不得不稍微深究了下。

同源策略

在介绍 CORS 之前,我们必须先了解下同源策略,因为正是有了同源策略,所以才有了 CORS 机制,换句话说:其实同源策略才是阻断请求的罪魁祸首,而 CORS 反而是通过一定配置,允许浏览器放行这些请求的机制。

我们来假设一共情况,一个攻击者希望得到用户内网的敏感数据,而内网意味着只能通过被攻击用户的机器访问,不能在外部直接访问。攻击者别有用心地制造了一个攻击网站,通过钓鱼邮件,诱导用户点开我的网页,通过网页中的 JS 脚本直接在用户的内网机器发起请求,从而获取用户内网的敏感数据并将数据通过公网地址传送给他,这样他的攻击目的就达到了。设想一下,如果这里不仅仅是获取敏感数据,而是针对内网的一些财务系统,银行网站,问题是不是严重的多了?

可惜这在现实中是不可行的,假设我们正在访问攻击者的http://attack.com,浏览器的同源策略默认会限制该网页内的脚本只能访问与上述 URL同源的 URL,例如http://attack.com/a.js,而不能是https://baidu.com(或内网的财务系统等),对于内网的财务系统来说,同源策略很好的保护了他们免受恶意攻击。

那么 URL 需要满足哪些条件才会被判定为同源呢?标准答案可以参考:浏览器的同源策略 - Web 安全 | MDN,我这里额外列出来。

  • 协议(Protocol):如HTTPHTTPS
  • 端口(Port):80(HTTP默认端口)443(HTTPS默认端口)
  • 主机(Host):www.baidu.comwww.google.com

也许有人会问,协议和端口是不是重复了?其实没有,毕竟 443 只是 HTTPS 的默认端口,但即使你手动指定提供 HTTPS 的服务器的监听端口为 80 或其他端口,只要配置正确,同样是可以通过 HTTPS 协议进行传输的。

安全的问题解决了,但是这样的策略不符合我们的生产实际呀,在前后端分离开发的背景下,前端部分往往单独部署在静态资源服务器上,如static.domain.com,而后端服务往往是由另一个主机提供的,如api.domain.com,让我们回想一下刚刚的同源策略,前端页面上的 JS 脚本期望访问api.domain.com,却发现被阻止了!这不是我们所希望看到的,这样正常的请求应该被允许,怎么办?CORS 机制出现了。

CORS

CORS(Cross-origin resource sharing)中文译名:跨源(域)资源共享。是一种针对浏览器同源策略而定制的机制。它允许服务端来指定哪些主机可以从这个服务端加载资源。

简单来说,CORS 的配置是由服务的来进行的,也就是说,浏览器能不能通过 JS 访问这个后端服务,是由后端服务自己来决定的,具体的实现是这样的:浏览器在发起正式的请求前,会先向后端服务发送一个“预检”请求,里面包含了当前页面的“源(Origin)”,后端服务在接收到后会根据开发者的配置,返回一个清单,里面包含了允许的源、方法、标头等信息。浏览器在收到后会进行对比,如果当前源的访问是被后端允许的,那么正式的请求就会被发送,反之亦然。

当然,这里是一种极其简化的描述,实际上官方文档的解释远比我说的好得多:跨源资源共享(CORS) - HTTP | MDN,如果有能力,可以直接阅读官方文档。

需要注意的是,这是一种仅限于浏览器的安全策略,毕竟最终是否允许发送请求的校验是在浏览器本身进行的,如果直接通过接口工具,上面的所有问题都没有了。这也是为什么接口调的好好的,放到浏览器就出问题了的原因,嘿嘿。

到这里,我们通过一定的设置就可以让开发进行下去了,比如允许来源全部设置为“*”,当然,这在生产环境中是不推荐的,因为这样就失去了浏览器提供的对网站的保护。