csrf是什么
来自wiki
跨站请求伪造(英语:Cross-site request forgery),也被称为 one-click attack 或者 session riding,通常缩写为 CSRF 或者 XSRF,是一种挟制用户在当前已登录的Web应用程序上执行非本意的操作的攻击方法。跟跨网站脚本(XSS)相比,XSS利用的是用户对指定网站的信任,CSRF利用的是网站对用户网页浏览器的信任。 简单的身份验证只能保证请求发自某个用户的浏览器,却不能保证请求本身是用户自愿发出的。
一个例子
网上很多是银行转账的例子,这里再编一个新的。用户访问www.example.com,看到大抽奖,毫不犹豫就点击了
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>喜迎5G上市幸运大抽奖</title>
</head>
<body>
<form method="get" action="http://www.xx-ads.com/click.do">
<input type="hidden" name="ad_id" value="xxx">
<input type="hidden" name="from_id" value="yyy">
<input type="button" onclick="submit()" value="点击赢大奖">
</form>
</body>
然后就给某个广告投放者创造了一次点击(当然广告平台有反作弊机制啦)。
解决
csrf根源问题在于,服务器对请求来源不加以区分,即无条件信任来自浏览器的请求。
验证referer字段
http协议定义了referer字段,用来表示当前请求的来源,具体是url地址。使用referer字段可以简单判断请求来源。但是,referer字段很容易伪造。
自定义csrf token字段
因为A站点的页面不能获取B站点的页面内容、cookies,因此可以在提交页面上增加token字段。
- 生成表单的时候增加hidden字段,用来存放服务器生成的token。提交表单的时候一起提交
<input type="hidden" name="csrf-token" value="5QhM72KX9zjHO03V">
- cookies返回token,因为获取不到其他站点的cookie。
- token放在自定义的header,比如
X-CSRF-TOKEN。
不管token是放在hidden、cookies还是自定义header,都要在提交的时候在服务器端验证token的有效性。
服务器端可以使用filter、切面等方式统一验证token。
用户端防范
关键站点(比如跟💴相关的)使用完之后及时退出。这样伪造请求发出去,通常会挂在被攻击站点的登录态校验。
实战
nginx限制referer字段
nginx使用ngx_http_referer_module处理referer字段。
valid_referers none blocked server_names
*.example.com example.* www.example.org/galleries/
~\.google\.;
if ($invalid_referer) {
return 403;
}
根据valid_referers的结果,$invalid_referer返回0或者1。
valid_referers支持的方式有:
- none:referer字段为空
- blocked:正常的referer字段是
http://或者https://开头。其他为非正常的referer,比如Referer:xxx。通常是伪造请求,或者被防火墙修改。 - server_names:指定的服务器,支持通配符。
ps. nginx referer更多是用于简单的防盗链。
使用spring security
默认是开启csrf。
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable();
}
然后在form增加
<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/>
剩下的交给spring security框架。 注意这个是form表单。如果是ajax方式提交,要自己获取csrf token,并且提交。