跨域请求之CORS

浏览器同源政策

浏览器为了防止跨站恶意脚本攻击,而对脚本跨站请求所做的一种限制,称为浏览器同源策略(Same-origin policy)。

该策略虽然能够防止跨站脚本的攻击,但同时也限制了跨站信息的沟通。而对于大型网站系统来说,基于脚本跨站通信场景(基于javascript的ajax请求)实实在在存在的。

为了绕过或放松同源策略的限制,出现了一些列技术,如:JSONP、WebSockets、Cross-origin Resource Sharing(Cors),Cross-document-messaging等等。

什么是cors

Cors全称跨域资源共享(Cross-origin resource sharing),它是w3c推荐的一种跨站资源资源共享标准。

其主要原理和实现步骤为:(假设www.a.com站请求链接www.b.com/list)

1、浏览器会将跨站Request的Header中带上Origin:www.a.com,表明该请求来至哪一个站。

2、www.b.com的服务端在接受到请求后,会在Response Header中携带一堆,Access-Control-Allow-*信息,表明自己可以允许哪些站访问,以及可以使用什么方式访问(是post还是get)等信息。

3、浏览器基于Response中的的Access-Control-Allow-*信息,判断www.a.com是否有被允许获取www.b.com/list返回的结果信息。如不允许则ajax请求出错。

值得注意的是,如果www.b.com/list没有返回任何Access-Control-Allow-*信息,则说明其不支持cors,浏览器同样将www.a.com的ajax请求设为失败。

原理时序图如下:

Preflight预检

由于跨站请求时,无论源站是否被允许访问,其请求都会到达服务端造成一次服务端响应,只是最终返回结果在源站无权限时,浏览器会对其屏蔽掉结果。这种机制在查询类型访问时(如get),并不会对服务端数据造成安全微信。但如果对应的请求是对服务端数据进行修改、删除操作,那么即便浏览器不对源站开发结果,请求本身依然对服务单数据造成了伤害。

为了避免这种安全风险,故当源站对目标站进行不是简单类型请求时,浏览器会预先进行一次预检请求(options请求),判断源站请求有请求资格时,再做实际请求。

具体浏览器会在什么情况下做预检,其逻辑如下:

图中所谓的standard content type是指:application/x-www-form-urlencoded,multipart/form-data,text/plain 这几种

cors相关的header列表
Request headers

Origin:浏览器自动添加
Access-Control-Request-Method:预检时浏览器自动携带
Access-Control-Request-Headers:

Response headers

Access-Control-Allow-Origin:被允许访问的站点
Access-Control-Allow-Credentials:跨站访问的请求允许携带cookie
Access-Control-Expose-Headers:response中允许暴露给跨站脚本的头
Access-Control-Max-Age:允许浏览器缓存这些allow信息的时长。
Access-Control-Allow-Methods:允许跨站请求的方法
Access-Control-Allow-Headers:允许跨站请求携带的头

强调说明

1、一般情况下服务端可以Access-Control-Allow-Origin设置为通配符,比如*,即允许所有站点访问。

但如果即配置了Access-Control-Allow-Origin:*,又配置了Access-Control-Allow-Credentials:true,这种情况是不允许的,如果这样配置后,浏览器会视作跨站访问失败。

因为以上配置这会导致防止csrf攻击的 token泄露。

简单说csrf攻击就是用骗用户点击真实合法的链接,导致的安全风险。比如,银行有个转款请求:www.bank.com/transfer/money/from/jack/to/tom

如果jack登录银行后未退出,这时恶意网站有个图片<img src="www.bank.com/transfer/money/from/jack/to/tom">,jack在浏览该网站时,不知情的情况下点击了该图片,就会触发以上请求,由于点击是用户自愿行为,浏览器会当作正常请求并携带www.bank.com的登录cookie到后台,实现转款。

为了防止这种攻击,可以在server端通过检查请求refer,判断请求的合法性,但refer本身容易被黑客篡改。另外一种靠谱的方式就是使用token,服务端每次返回表单给用户时,生成一个token一并返回给用户,用户提交表单时,携带该token,然后服务端除了校验cookie中的登录凭证之外,还要校验token的合法性,从而防止了csrf攻击。

而如果某站点允许所有其他站访问,同时又允许请求携带cookie,在jack访问恶意站时,恶意站的脚本可以获取模仿用户请求获取到表单和token,然后诱骗用户点击,或者直接发送提交请求。

所以对于启用了Access-Control-Allow-Credentials的站点服务,必须要清楚的声明,其到底允许哪些站可以带跨站访问服务。

2、Access-Control-Allow-Credentials
允许跨站请求携带cookie,携带的是被请求域的cookie,跨站脚本依然无法直接读取操作cookie内容

cors同jsonp的对比

jsonp基本没有浏览器版本限制。jsonp只能做get请求,而且有xss风险。访问控制不够精确,基本对所有站点都是开放的,所以不好做跨域写。

参考链接

http://www.html5rocks.com/en/tutorials/cors/
http://www.freebuf.com/articles/web/18493.html
https://zh.wikipedia.org/wiki/%E8%B7%A8%E7%AB%99%E8%AF%B7%E6%B1%82%E4%BC%AA%E9%80%A0
https://en.wikipedia.org/wiki/Cross-originresourcesharing
https://en.wikipedia.org/wiki/Same-origin_policy