1.什么是Csrf攻击
关于什么是csrf攻击,这里不多赘述,可以参考下面的文章,写的不错。
浅谈CSRF攻击方式,今天这里主要记录如何防御csrf攻击。
2.防御csrf攻击
这里csrf攻击的防御采用以下方案:在用户发送请求之前,先通过ajax请求访问后台,生成一个随机数作为一个token,并将这个token保存在session,同时返回到前台页面,然后前台页面将这个token放在请求中,发送到后台,被过滤器拦截,判断请求中带的token和session中的token进行匹配,如果一致,则转发请求到真正的controller中处理。下面给出代码,因为这是自己这两天和朋友闲着做的一个项目,准备先写后台接口,还没有前台代码,所以这里只给出后台代码。
生成token的方法
/**
* 工具类
* @author HXY
*/
public class Utils {
/**
* 生成固定长度的token:包含大小写字母和数字
* @param length 需要的token的长度
* @return
*/
public static String createToken(int length) {
//token
StringBuffer token = new StringBuffer();
SecureRandom random = new SecureRandom();
for (int i = 0; i < length; i++) {
int num = random.nextInt(3);
switch (num) {
case 0:
//生成0-9之间的数字
token.append((int)random.nextFloat() * 9);
break;
case 1:
//生成A-Z之间的字符
token.append((char) (65 + (int) (random.nextFloat() * 26)));
break;
case 2:
//生成a-z之间的字符
token.append((char) (97 + (int) (random.nextFloat() * 26)));
break;
default:
break;
}
}
return token.toString();
}
}
CsrfController ,所有请求在到达后台前,先访问这个controller,获取token,并且将token绑定在请求中,然后发送到后台。这里省去了前台代码。
/**
* CsrfController 生成token,存储在session中并返回到前台
* @author HXY
* @version 1.0
*/
@RestController
public class CsrfController {
private final static Logger logger = LoggerFactory.getLogger(CsrfController.class);
/**
* 在服务端保存token,并将生成的token返回到前台
* @param request
* @return 生成的token
*/
@GetMapping("/honeybee/csrf")
@ResponseBody
public HoneyResult createToken(HttpServletRequest request) {
logger.info("begin create csrfToken...");
String token = Utils.createToken(128);
//保存在session中
request.getSession().setAttribute("token",token);
logger.info("end create csrfToken...");
//作为json格式返回到页面
return new HoneyResult(token);
}
}
过滤器,拦截所有请求,验证token。
/**
* 防止csrf攻击的过滤器
* @author HXY
*/
@WebFilter(urlPatterns = "/*", filterName = "csrfFilter")
public class CsrfFilter extends OncePerRequestFilter {
private final static String REQUEST_METHOD_POST = "POST";
private final static String REQUEST_METHOD_GET = "GET";
private final static Logger logger = LoggerFactory.getLogger(CsrfFilter.class);
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
logger.info("Begin csrf...");
//服务器端存储的token
String token_server = (String) request.getSession().getAttribute("token");
//客户端请求的token
String token_client = (String) request.getAttribute("token");
//如果是post请求,则做csrf校验
if (REQUEST_METHOD_POST.equals(request.getMethod())) {
if (null != token_client && null != token_server && token_client.equals(token_server)) {
filterChain.doFilter(request, response);
}
logger.info("Csrf parameter is invalid...");
}else if (REQUEST_METHOD_GET.equals(request.getMethod())){
filterChain.doFilter(request,response);
}
logger.info("End Csrf...");
}
}
代码是可以运行的。还没有页面,没有测试能不能有效防御csrf攻击。后续会进行严格测试,并进行补充。