CORS跨域资源共享漏洞
原理
其实大体上原理和jsonp攻击差不多,也是利用了浏览器跨域获取信息。在jsonp中我们说过,在js中发起http请求,比如用XMLHttpRequest 或 fetch等,默认只能获取同源的域中的资源,无法跨域获取资源。但如果服务器本身同意了跨域请求,那也可以在前端js中跨域请求资源。那么服务器如何告知浏览器,它支持跨域请求呢?这里就需要引入CORS(跨域资源共享)这个概念了。
CORS原理
我们先来回顾一下CORS跨域获取资源的过程:
CORS定义了两种跨域请求:简单请求和非简单请求。简单跨域请求就是使用设定的请求方式请求数据,而非简单跨域请求则是在使用设定的请求方式请求数据之前,先发送一个OPTIONS预检请求,验证请求源是否为服务端允许的源,只有预检通过后才会再发送一次请求用于数据传输。
CORS运行机制:第一步,在浏览器发起请求时,自动在请求头中添加了Origin字段,浏览器定义了js不可以修改请求头中的Origin字段,确保了在浏览器中发起请求的Origin确实为用户真实的源地址,而非伪造的地址。第二步,服务器通过验证Origin字段来判断请求是否被允许,从而实现浏览器的跨域访问。
CORS相关名词解释
服务端需要在响应头中包含一些CORS相关的字段来告诉浏览器,该服务器使用的CORS策略是怎么样的。
Access-Control-Allow-Origin
:该字段是必须的。设置了哪些来源才可以跨域访问服务器上的资源。
Access-Control-Allow-Credentials
:该字段可选,值是一个布尔值,表示是否允许发送Cookie。默认情况下,Cookie不包含在CORS请求中,当设置为True时,表示服务器允许Cookie可以包含在请求中一起发送给服务器。这个值也只能设置为True,如果服务器不允许浏览器发送Cookie,直接删除该字段即可。
Access-Control-Expose-Headers
:该字段可选。CORS请求时,AJAX对象只能从响应头中拿到6个基本字段:Cache-Control,Content-Language,Content-Type,Expires,Last-Modified,Pragma。如果想要拿到其他字段,就必须在Access-Control-Expose-Headers
中指定。
Access-Control-Request-Methods
:一般为POST,GET,OPTIONS
利用方式
依旧是一个正常的服务端,有一个返回个人信息的api接口,只不过这次在响应头中增加了Access-Control-Allow-Origin:*
,接受任意来源的跨域请求。代码如下
1 |
|
正常的页面的前端,直接用ajax跨域请求这个api即可
1 |
|
可以看到请求的来源和服务端不在同一个域,通过服务器返回响应头中的Access-Control-Allow-Origin: *
实现了跨域请求。
接下去我要实现一个恶意页面evil.php,让受害者访问,第11行恶意页面先以用户的名义访问了正常页面的获取个人信息的接口,由于这个接口允许任何来源的跨域跨域请求,所以给恶意页面所在8088端口的域正常返回了信息。在第13行,拿到用户的个人信息后,恶意页面将该数据上传,服务器上就拿到了用户的个人信息。由第1行中的file参数接收并存储在cors.txt文件中
1 |
|
通过这三个请求可以看到完整的攻击路径。
防御方案
- 不要配置
Access-Control-Allow-Origin
为通配符“*”,而且更重要的是,要严格效验来自请求数据包中的”Origin” 的值。当收到跨域请求的时候,要检查”Origin” 的值是否是一个可信的源, 还要检查是否为 null - 避免使用
Access-Control-Allow-Credentials: true
- 减少
Access-Control- Allow-Methods
所允许的方法
漏洞挖掘方式
- 通过js爬虫或者流量分析的方式,特别是涉及到敏感信息的接口,比如获取个人信息的接口。检查响应头中的
Access-Control-Allow-Origin
的值是否为通配符“*” - 有些服务端的代码会这样写
1 |
|
不管Origin中的域名是什么,服务端都接收这些跨域请求。这和设置为“*”是一样的效果。在挖掘中可以尝试发送一个和正常域名不相同的域名,存在CORS漏洞的域名为aaa.com
,我们可以设置一个值为bbb.com
的Origin,如果服务端的Access-Control-Allow-Origin
值也返回了bbb.com
或者*
,那么需要特别注意,很有可能存在CORS漏洞,值得进一步验证。