CORS 漏洞
漏洞简述
跨域资源共享(CORS)是一种放宽同源策略的机制,以使不同的网站可以跨域获取数据。CORS非常有用,可以共享许多内容,不过这里存在风险。因为它完全是一个盲目的协议,只是通过HTTP头来控制的。如果Access-Control-Allow-Origin可控,且Access-Control-Allow-Credentials为true, 那么就可以利用一个可控的网站来窃取一个人的个人隐私信息、敏感信息,请求用户主页等。
CORS的跨域获取资源过程:
CORS定义了两种跨域请求:简单跨域请求 和 非简单跨域请求。简单跨域请求就是使用设定的请求方式请求数据,而非简单跨域请求则是在使用设定的请求方式请求数据之前,先发送一个OPTIONS预检请求,看服务端是否允许客户端发送非简单请求。只有"预检"通过后才会再发送一次请求用于数据传输。
当我们需要发送一个跨域请求的时候,浏览器会首先检查这个请求,如果它是简单跨域请求,浏览器就会立刻发送这个请求。如果它是非简单跨域请求,这时候浏览器不会马上发送这个请求,而是有一个跟服务器预检验证的过程。
简单跨域请求
当一个跨域请求发送简单跨域请求时包括:
请求方法为HEAD、GET、POST;
请求头只有4个字段,Accept、Accept-Language、Content-Language、Last-Event-ID。如果请求头还设置了Content-Type,则其值只能是 application/x-www-form-urlencoded,multipart/form-data,text/plain。
只有同时满足以上两个条件时,才是简单请求,否则为非简单请求。
在简单请求中,浏览器进行跨域请求,会在请求中携带Origin,表面这是一个跨域。服务端会在接收中,通过自己的跨域规则进行验证。通过Access-Control-Allow-Origin和Access-Control-Allow-Methods ,如果验证成功,则会返回资源内容,如果验证失败,则返回403状态。
非简单跨域请求
下面简单分析一下非简单跨域请求的过程。浏览器先发送一个OPTIONS方法的预检请求。带有如下字段:
Origin: 普通的HTTP请求也会带有,在CORS中专门作为Origin信息供后端比对,表明来源域。
Access-Control-Request-Method: 接下来请求的方法,例如PUT、DELETE等等
Access-Control-Request-Headers: 自定义的头部,所有用setRequestHeader方法设置的头部都将会以逗号隔开的形式包含在这个头中
然后如果服务器配置了CORS,会返回对应对的字段,具体字段含义在返回结果是一并解释。
Access-Control-Allow-Origin: 允许进行跨域请求的域名
Access-Control-Allow-Methods: 允许进行跨域请求的方式
Access-Control-Allow-Headers: 允许进行跨区请求的头部
然后浏览器再根据服务器的返回值判断是否发送非简单请求。然后服务器处理完请求之后,会再返回结果中加上如下控制字段:
Access-Control-Allow-Origin: 允许跨域访问的域,可以是一个域的列表,也可以是通配符"*"。这里要注意Origin规则只对域名有效,并不会对子目录有效。即http://foo.example/subdir/ 是无效的。但是不同子域名需要分开设置,这里的规则可以参照同源策略
Access-Control-Allow-Credentials: 是否允许请求带有验证信息
Access-Control-Expose-Headers: 允许脚本访问的返回头,请求成功后,脚本可以在XMLHttpRequest中访问这些头的信息
Access-Control-Max-Age: 缓存此次请求的秒数。在这个时间范围内,所有同类型的请求都将不再发送预检请求而是直接使用此次返回的头作为判断依据,非常有用,大幅优化请求次数
Access-Control-Allow-Methods: 允许使用的请求方法,以逗号隔开
Access-Control-Allow-Headers: 允许自定义的头部,以逗号隔开,大小写不敏感
然后浏览器通过返回结果的这些控制字段来决定是将结果开放给客户端脚本读取还是屏蔽掉。如果服务器没有配置CORS,返回结果没有控制字段,浏览器会屏蔽脚本对返回信息的读取。
所以,网站可以通过发送以下HTTP响应头部来启用CORS:
Access-Control-Allow-Origin: https://example.com
这样的话,就可以允许指定的源(http://example.com)来跨域请求服务器端的资源,并且服务器会响应。在默认情况下,发送跨域请求时不会携带cookie或其他凭据。因此,它不能用于窃取与用户相关的敏感信息(如CSRF令牌)。不过,网站服务器可以使用以下头部来启用凭据传输:
Access-Control-Allow-Credentials:true
这样浏览器在请求数据的时候就需要带上cookie,服务器在回应的时候也会返回Cookie等一些信息进行双向认证。
实现对单个域的信任是非常容易的事情。不过,如果需要信任多个域的话,那该怎么办呢?根据相关规范的建议,只需列出相关的域,并用空格加以分隔即可,例如:
Access-Control-Allow-Origin:http://a.example.com http://example.com
但是,没有哪个浏览器真正支持这一特性。
于是,我们可以通过使用通配符来信任所有子域,具体方法是:
Access-Control-Allow-Origin: *.example.com
可是有一些偷懒的程序员,将Access-Control-Allow-Origin设置为允许来自所有域*的跨域请求。
Access-Control-Allow-Origin:*
这样,所有的网站都可以对其进行跨域资源请求了,这是非常危险的。不过先别高兴的太早。其实这里在设计的时候有一个很好的限制。xmlhttprequest发送的请求需要使用“withCredentials”来带上cookie,如果一个目标域设置成了允许任意域的跨域请求,这个请求又带着cookie的话,这个请求是不合法的。(就是如果需要实现带cookie的跨域请求,需要明确的配置允许来源的域,使用任意域的配置是不合法的)浏览器会屏蔽掉返回的结果。javascript就没法获取返回的数据了。这是CORS模型最后一道防线。假如没有这个限制的话,那么javascript就可以获取返回数据中的Cookie和CSRF Token,以及各种敏感数据。这个限制极大的降低了cors的风险。
如下,这是不允许的:
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true
这时,将在浏览器控制台中收到以下错误消息:当凭证标志为true时,无法在Access-Control-Allow-Origin中使用通配符。
漏洞利用
漏洞修复
Last updated
Was this helpful?