《网络安全学习》 第七部分-----跨域资源共享(CORS)漏洞

本博客内容部分来自于漏洞银行公开的学习视频之中,感谢漏洞银行的无偿技术分享

第一部分:浏览器同源策略

学习跨域资源共享之前,首先要了解到的是浏览器同源策略这个概念。这样便于后续的跨域资源共享漏洞的理解和运用。
同源策略(Same origin policy)是一种约定,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,则浏览器的正常功能可能都会受到影响。可以说Web是构建在同源策略基础之上的,浏览器只是针对同源策略的一种实现。
– 摘自:百度百科
所谓同源是指,域名,协议,端口相同。

  • 下面,以某个URL地址为例,来直观的了解同源策略。
    《网络安全学习》 第七部分-----跨域资源共享(CORS)漏洞_第1张图片
其中:第一行和第二行均成功,是因为这两个URL相对于上面的URL来说只是不同的目录和不同的文件,但是它们相对于上面的URL来说却是`同一个域名,同样的协议,同样的端口.`

第二部分:两种跨域的方法 1

  • JSONP跨域请求
  • CORS跨域请求

由于目前的很多项目都是前后端分离的,但是由于浏览器同源策略的缘故,因此就一定会出现跨域的问题,而处理跨域的方法有很多种类,上述只是两种处理跨域的方式。而处理跨域也就意味着可能会出现-跨域资源共享(CORS)漏洞.

2.1 JSONP跨域的处理方式

JSONP其实已经落后了,因为通过JSONP来处理跨域的话,只能处理get请求,其他的请求方式根本无法实现。这里不做重点阐述

2.2 CORS跨域的处理方式

CORS是一个W3C标准,全称是"跨域资源共享"(Cross-origin resource sharing)。它允许浏览器向跨源(协议 + 域名 + 端口)服务器,发出XMLHttpRequest请求,从而克服了AJAX只能同源使用的限制。
CORS 需要浏览器和服务器同时支持。目前,所有浏览器都支持该功能,IE 浏览器不能低于 IE10。

CORS 背后的基本思想,就是使用自定义的 HTTP 头部让浏览器与服务器进行沟通,从而决定请求或响应是应该成功,还是应该失败。

整个 CORS 通信过程,都是浏览器自动完成,不需要用户参与。对于开发者来说,CORS 通信与同源的 AJAX 通信没有差别,代码完全一样。浏览器一旦发现 AJAX 请求跨源,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,但用户不会有感觉。

因此,实现 CORS 通信的关键是服务器。只要服务器实现了 CORS 接口,就可以跨源通信。

2.2.1 浏览器将CORS请求分成两类:
  • 简单请求(simple request)
  • 非简单请求(not-so-simple request)。
只要同时满足以下两大条件,就属于简单请求

一、 请求方法是以下三种方法之一:
HEAD
GET
POST
二、HTTP的头信息不超出以下几种字段:
Accept
Accept-Language
Content-Language
Last-Event-ID
Content-Type:只限于三个值 application/x-www-form-urlencoded、multipart/form-data、text/plain

凡是不同时满足上面两个条件,就属于非简单请求。

而浏览器对这两种请求的处理,是不一样的。

简单请求

  1. 在请求中需要附加一个额外的 Origin 头部,其中包含请求页面的源信息(协议、域名和端口),以便服务器根据这个头部信息来决定是否给予响应。例如:Origin: http://www.tomatocc.cn
  2. 如果服务器认为这个请求可以接受,就会在 Access-Control-Allow-Origin 头部中回发相同的源信息(如果是公共资源,可以回发 * )。例如:Access-Control-Allow-Origin:http://www.tomatocc.cn
  3. 没有这个头部或者有这个头部但源信息不匹配,浏览器就会驳回请求。正常情况下,浏览器会处理请求。注意,请求和响应都不包含 cookie 信息。
  4. 如果需要包含 cookie 信息,ajax 请求需要设置 xhr 的属性 withCredentials 为 true,服务器需要设置响应头部 Access-Control-Allow-Credentials: true。

非简单请求

浏览器在发送真正的请求之前,会先发送一个 Preflight 请求给服务器,这种请求使用 OPTIONS 方法,发送下列头部:

  • Origin:与简单的请求相同。
  • Access-Control-Request-Method: 请求自身使用的方法。
  • Access-Control-Request-Headers: (可选)自定义的头部信息,多个头部以逗号分隔。

第三部分:跨域资源共享(CORS)漏洞

上面知道了处理跨域的时候可以通过CORS来处理跨域,但是,如果你的Access-Control-Allow-Origin是可控的(本人理解为如果在response组装的时候将Access-Control-Allow-Origin的值直接从请求中获取,并不是指定某些ip和端口,那么便存在可控。),且Access-Control-Allow-Credentials为true(允许cookie跨域),那么就可以利用一个可控的网站来窃取一个人的个人隐私信息。

下面通过一副图片来看一下利用跨域资源共享(CORS)漏洞来获取站点信息的原理
《网络安全学习》 第七部分-----跨域资源共享(CORS)漏洞_第2张图片

该图的盗取过程基本是如下几个步骤:
1.用户登陆了一个存在跨域的网站,并且这个网站的跨域处理方式为CORS,并且开发人员处理跨域的时候满足cookie跨域和Origin是可控的。那么用户在登陆之后这个网站后,网站便会将一些cookie信息保存在浏览器中。
2. 这个时候我们向该用户发送一个钓鱼网站,用户点开这个钓鱼网站之后,钓鱼网站会向服务器发送一个Ajax异步请求,然后将返回的响应发送给攻击者。

3.1 CORS漏洞与CSRF漏洞对比
相同点:

  • 都要借助第三方网站
  • 都要借助ajax的异步过程
  • 一般都需要用户登陆

不同点:

  • 第三方网站可以利用CORS漏洞读取到受害者的敏感信息
  • 第三方网站可以利用csrf漏洞可以替受害者完成诸如转账等敏感操作
  • 一般有CORS漏洞的地方都有csrf漏洞

实战演示:
1) 使用burp来寻找一些可能存在CORS漏洞的站点。
我们将所有的请求头统一增加一个Origin参数,来满足跨域
《网络安全学习》 第七部分-----跨域资源共享(CORS)漏洞_第3张图片
2) 在HTTP History中做一些过滤处理
《网络安全学习》 第七部分-----跨域资源共享(CORS)漏洞_第4张图片
3)然后我们去访问一些网站,这样就可以看出来哪些网站是存在CORS漏洞。

3.2 "null"源问题

从上面的文章中可以知道,处理跨域到时候如果Origin可控,则会出现CORS漏洞,那么如果攻击者在发送请求的时候将Origin的值设置为null会发生什么?

下图摘自 漏洞银行《网络安全学习》 第七部分-----跨域资源共享(CORS)漏洞_第5张图片

CORS的规范中特意的提到了“NULL”源的概念,触发这个源是为了页面跳转或者是来自本地HTML文件。
目标应用可能会接收“null”源,并且这个可能被测试者(或者攻击者)利用,任何网站很容易使用沙盒iframe来获取“null 源”

第四部分:跨域资源共享(CORS)漏洞防御

上面讲解了CORS的漏洞原理和利用方式,那么该介绍怎么防护了。

实战代码:(存在CORS漏洞)

下方代码是我处理跨域问题的方式。

package com.demo.invocation;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.jfinal.aop.Interceptor;
import com.jfinal.aop.Invocation;
import com.jfinal.core.Controller;
import com.jfinal.log.Log;

public class AjaxInterceptor implements Interceptor {
	
	private static Log log = Log.getLog(AjaxInterceptor.class);

	public void intercept(Invocation inv) {
		Controller controler = inv.getController();
		
		/** 不安全的方案**/
		// 获得请求头信息
		HttpServletRequest req = controler.getRequest();
		// 获取请求头的 Origin 参数
		String origin = req.getHeader("Origin");
		// 获取请求头的 Access-Control-Allow-Headers参数
		//String headers= req.getHeader("Access-Control-Allow-Headers");

		// 获得响应信息
		HttpServletResponse res = controler.getResponse();
		
		//**组装响应头**//*
		//客户端地址                                                              
		res.addHeader("Access-Control-Allow-Origin", origin);// *号不能满需带cookie的跨域请求
		// 设置响应头q
		//res.addHeader("Access-Control-Allow-Headers", headers);
		// 设置响应方法  允许所有方法进行跨域访问
		res.addHeader("Access-Control-Allow-Methods", "*");
		//设置响应预检命令的缓存时间(设置后,浏览器在该时间内只会预检一次) 单位秒
		//res.addHeader("Access-Control-Max-Age", "3600");
		// cookie跨域的问题  请求头解决方式
		res.addHeader("Access-Control-Allow-Credentials", "true");
		
		
		
		
		/** 下面为解决方案**/
/*		// 获得响应信息
		HttpServletResponse res = controler.getResponse();
		res.addHeader("Access-Control-Allow-Origin", "http://127.0.0.1:8082");// *号不能满需带cookie的跨域请求
		res.addHeader("Access-Control-Allow-Methods", "GET");
		//要尽可能的返回"Vary: Origin"这个头部,以避免攻击者利用浏览器缓存
		res.addHeader("Vary", "Origin");*/
		
		
		inv.invoke();
	}

}

图中红色方框的代码就明显满足CORS漏洞的产生,当然我是用漏扫工具也扫出来了这个问题。
《网络安全学习》 第七部分-----跨域资源共享(CORS)漏洞_第6张图片
漏扫结果:报出了CORS漏洞
《网络安全学习》 第七部分-----跨域资源共享(CORS)漏洞_第7张图片
并且之前的过滤也成功实现(存在CORS漏洞)
在这里插入图片描述

后面,我将不安全的方案注释掉,使用安全的方案,可以看到已经不存在漏洞了。

防御方案:

  • 不要配置"Access-Control-Allow-Origin" 为通配符 “*” ,而且更为重要的是,要严格效验来自请求数据包中的“Origin”的值。当收到跨域请求的时候,要检查“Origin” 的值是否是一个可信的源,还要检查是否为null。
  • 避免使用 “Access-Control-Allow-Credentials :true” (请求中带cookie)
  • 减少“Access-Control-Allow-Methods”所允许的方法(只需要配置你所需要的即可)
个人总结

1)如果响应的Access-Control-Allow-Origin值是从request中的Origin参数中获取的话,那么就会存在CORS漏洞。即下列代码

		HttpServletRequest req = controler.getRequest();
		// 获取请求头的 Origin 参数
		String origin = req.getHeader("Origin");
		HttpServletRequest req = controler.getRequest();
	    res.addHeader("Access-Control-Allow-Origin", origin)

2)如果响应的Access-Control-Allow-Origin值是 * 号,Access-Control-Allow-Credentials值为true的话,那么也不存在CORS漏洞。

		HttpServletResponse res = controler.getResponse();	
	    //**组装响应头**//*                                              
		res.addHeader("Access-Control-Allow-Origin", "*");
		res.addHeader("Access-Control-Allow-Credentials", "true");

3) 但是从最安全的角度来讲,使用上面的建议来预防CORS漏洞是更为安全的。

下面为我的真实解决方案:

		// 获得请求头信息
		HttpServletRequest req = controler.getRequest();
		// 判断是否为null源漏洞
		String origin = req.getHeader("Origin");
		if(origin.equals("null")) {
			System.out.println("null 源漏洞  处理异常");
			
		} else {
			// 获得响应信息
			HttpServletResponse res = controler.getResponse();
			res.addHeader("Access-Control-Allow-Origin", "http://127.0.0.1:8082");// 指定可信的源
			res.addHeader("Access-Control-Allow-Methods", "GET");
			//要尽可能的返回"Vary: Origin"这个头部,以避免攻击者利用浏览器缓存
			res.addHeader("Vary", "Origin");
			
			inv.invoke();
		}

参考资料:

W3C关于CORS规范

【网络安全学习】系列教程

《网络安全学习》第一部分-----初识OWASP
《网络安全学习》第二部分-----SQL注入学习
《网络安全学习》第三部分-----XSS攻击系列学习
《网络安全学习》第四部分-----SSRF服务器端请求伪造
《网络安全学习》第五部分-----远程代码执行漏洞
《网络安全学习》 第六部分-----文件上传漏洞
《网络安全学习》 第七部分-----跨域资源共享(CORS)漏洞
《网络安全学习》 第八部分-----《网络安全学习》 第八部分-----越权漏洞详解
《网络安全学习》 第九部分----CSRF(跨站请求伪造)漏洞详解


  1. 关于跨域
    原理,及相关处理方案可以参考我的另一篇博客
    《实战开发》AJAX跨域问题处理 ↩︎

你可能感兴趣的:(开发栈----网络安全学习)