C语言学习网

利用spring security防御csrf攻击的原理是什么

发表于:2022-08-11 作者:安全数据网编辑
编辑最后更新 2022年08月11日,这篇文章给大家介绍利用spring security防御csrf攻击的原理是什么,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。什么是csrf?csrf又称跨域请求伪造,攻击方通过

这篇文章给大家介绍利用spring security防御csrf攻击的原理是什么,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。

什么是csrf?

csrf又称跨域请求伪造,攻击方通过伪造用户请求访问受信任站点。CSRF这种攻击方式在2000年已经被国外的安全人员提出,但在国内,直到06年才开始被关注,08年,国内外的多个大型社区和交互网站分别爆出CSRF漏洞,如:NYTimes.com(纽约时报)、Metafilter(一个大型的BLOG网站),YouTube和百度HI......而现在,互联网上的许多站点仍对此毫无防备,以至于安全业界称CSRF为"沉睡的巨人"。

举个例子,用户通过表单发送请求到银行网站,银行网站获取请求参数后对用户账户做出更改。在用户没有退出银行网站情况下,访问了攻击网站,攻击网站中有一段跨域访问的代码,可能自动触发也可能点击提交按钮,访问的url正是银行网站接受表单的url。因为都来自于用户的浏览器端,银行将请求看作是用户发起的,所以对请求进行了处理,造成的结果就是用户的银行账户被攻击网站修改。

解决方法基本上都是增加攻击网站无法获取到的一些表单信息,比如增加图片验证码,可以杜绝csrf攻击,但是除了登陆注册之外,其他的地方都不适合放验证码,因为降低了网站易用性

相关介绍:

http://baike.baidu.com/view/1609487.htm?fr=aladdin

spring-servlet中配置csrf

             

在类中声明Csrf拦截器,用来生成或去除CsrfToken

import java.io.IOException;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import org.apache.logging.log4j.LogManager;import org.apache.logging.log4j.Logger;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.web.method.HandlerMethod;import org.springframework.web.servlet.ModelAndView;import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;import com.wangzhixuan.commons.scan.ExceptionResolver;import com.wangzhixuan.commons.utils.WebUtils;/** * Csrf拦截器,用来生成或去除CsrfToken *  * @author L.cm */public class CsrfInterceptor extends HandlerInterceptorAdapter { private static final Logger logger = LogManager.getLogger(ExceptionResolver.class);  @Autowired  private CsrfTokenRepository csrfTokenRepository;  @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {  HandlerMethod handlerMethod = (HandlerMethod) handler;  // 非控制器请求直接跳出  if (!(handler instanceof HandlerMethod)) {   return true;  }  CsrfToken csrfToken = handlerMethod.getMethodAnnotation(CsrfToken.class);  // 判断是否含有@CsrfToken注解  if (null == csrfToken) {   return true;  }  // create、remove同时为true时异常  if (csrfToken.create() && csrfToken.remove()) {   logger.error("CsrfToken attr create and remove can Not at the same time to true!");   return renderError(request, response, Boolean.FALSE, "CsrfToken attr create and remove can Not at the same time to true!");  }  // 创建  if (csrfToken.create()) {   CsrfTokenBean token = csrfTokenRepository.generateToken(request);   csrfTokenRepository.saveToken(token, request, response);   // 缓存一个表单页面地址的url   csrfTokenRepository.cacheUrl(request, response);   request.setAttribute(token.getParameterName(), token);   return true;  }  // 判断是否ajax请求  boolean isAjax = WebUtils.isAjax(handlerMethod);  // 校验,并且清除  CsrfTokenBean tokenBean = csrfTokenRepository.loadToken(request);  if (tokenBean == null) {   return renderError(request, response, isAjax, "CsrfToken is null!");  }  String actualToken = request.getHeader(tokenBean.getHeaderName());  if (actualToken == null) {   actualToken = request.getParameter(tokenBean.getParameterName());  }  if (!tokenBean.getToken().equals(actualToken)) {   return renderError(request, response, isAjax, "CsrfToken not eq!");  }  return true; }  private boolean renderError(HttpServletRequest request, HttpServletResponse response,    boolean isAjax, String message) throws IOException {  // 获取缓存的cacheUrl  String cachedUrl = csrfTokenRepository.getRemoveCacheUrl(request, response);  // ajax请求直接抛出异常,因为{@link ExceptionResolver}会去处理  if (isAjax) {   throw new RuntimeException(message);  }  // 非ajax CsrfToken校验异常,先清理token  csrfTokenRepository.saveToken(null, request, response);  logger.info("Csrf[redirectUrl]:\t" + cachedUrl);  response.sendRedirect(cachedUrl);  return false; } /**  * 用于清理@CsrfToken保证只能请求成功一次  */ @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,   ModelAndView modelAndView) throws Exception {  HandlerMethod handlerMethod = (HandlerMethod) handler;  // 非控制器请求直接跳出  if (!(handler instanceof HandlerMethod)) {   return;  }  CsrfToken csrfToken = handlerMethod.getMethodAnnotation(CsrfToken.class);  if (csrfToken == null || !csrfToken.remove()) {   return;  }  csrfTokenRepository.getRemoveCacheUrl(request, response);  csrfTokenRepository.saveToken(null, request, response); }}

声明Csrf过滤注解,通过标注来过滤对应的请求

import java.lang.annotation.ElementType;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;/** * Csrf过滤注解 * @author L.cm */@Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)public @interface CsrfToken { boolean create() default false; boolean remove() default false;}

建立实例对象(操作对象)

import java.io.Serializable;import org.springframework.util.Assert;public class CsrfTokenBean implements Serializable { private static final long serialVersionUID = -6865031901744243607L; private final String token; private final String parameterName; private final String headerName; /**  * Creates a new instance  * @param headerName the HTTP header name to use  * @param parameterName the HTTP parameter name to use  * @param token the value of the token (i.e. expected value of the HTTP parameter of  * parametername).  */ public CsrfTokenBean(String headerName, String parameterName, String token) {  Assert.hasLength(headerName, "headerName cannot be null or empty");  Assert.hasLength(parameterName, "parameterName cannot be null or empty");  Assert.hasLength(token, "token cannot be null or empty");  this.headerName = headerName;  this.parameterName = parameterName;  this.token = token; } public String getHeaderName() {  return this.headerName; } public String getParameterName() {  return this.parameterName; } public String getToken() {  return this.token; }}

过滤过程中需要的仓库

package com.wangzhixuan.commons.csrf;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;public interface CsrfTokenRepository { /**  * Generates a {@link CsrfTokenBean}  *  * @param request the {@link HttpServletRequest} to use  * @return the {@link CsrfTokenBean} that was generated. Cannot be null.  */ CsrfTokenBean generateToken(HttpServletRequest request); /**  * Saves the {@link CsrfTokenBean} using the {@link HttpServletRequest} and  * {@link HttpServletResponse}. If the {@link CsrfTokenBean} is null, it is the same as  * deleting it.  *  * @param token the {@link CsrfTokenBean} to save or null to delete  * @param request the {@link HttpServletRequest} to use  * @param response the {@link HttpServletResponse} to use  */ void saveToken(CsrfTokenBean token, HttpServletRequest request,   HttpServletResponse response); /**  * Loads the expected {@link CsrfTokenBean} from the {@link HttpServletRequest}  *  * @param request the {@link HttpServletRequest} to use  * @return the {@link CsrfTokenBean} or null if none exists  */ CsrfTokenBean loadToken(HttpServletRequest request); /**  * 缓存来源的url  * @param request request the {@link HttpServletRequest} to use  * @param response the {@link HttpServletResponse} to use  */ void cacheUrl(HttpServletRequest request, HttpServletResponse response); /**  * 获取并清理来源的url  * @param request the {@link HttpServletRequest} to use  * @param response the {@link HttpServletResponse} to use  * @return 来源url  */ String getRemoveCacheUrl(HttpServletRequest request, HttpServletResponse response);}

HttpSessionCsrfTokenRepository

package com.wangzhixuan.commons.csrf;import java.util.UUID;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import javax.servlet.http.HttpSession;import com.wangzhixuan.commons.utils.StringUtils;public final class HttpSessionCsrfTokenRepository implements CsrfTokenRepository { private static final String DEFAULT_CSRF_PARAMETER_NAME = "_csrf"; private static final String DEFAULT_CSRF_HEADER_NAME = "X-CSRF-TOKEN"; private static final String DEFAULT_CSRF_TOKEN_ATTR_NAME = HttpSessionCsrfTokenRepository.class   .getName().concat(".CSRF_TOKEN"); private static final String DEFAULT_CACHE_URL_ATTR_NAME = HttpSessionCsrfTokenRepository.class   .getName().concat(".CACHE_URL"); private String parameterName = DEFAULT_CSRF_PARAMETER_NAME; private String headerName = DEFAULT_CSRF_HEADER_NAME; private String sessionAttributeName = DEFAULT_CSRF_TOKEN_ATTR_NAME; private String cacheUrlAttributeName = DEFAULT_CACHE_URL_ATTR_NAME; /*  * (non-Javadoc)  *  * @see org.springframework.security.web.csrf.CsrfTokenRepository#saveToken(org.  * springframework .security.web.csrf.CsrfToken,  * javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)  */ public void saveToken(CsrfTokenBean token, HttpServletRequest request,   HttpServletResponse response) {  if (token == null) {   HttpSession session = request.getSession(false);   if (session != null) {    session.removeAttribute(this.sessionAttributeName);   }  }  else {   HttpSession session = request.getSession();   session.setAttribute(this.sessionAttributeName, token);  } } /*  * (non-Javadoc)  *  * @see  * org.springframework.security.web.csrf.CsrfTokenRepository#loadToken(javax.servlet  * .http.HttpServletRequest)  */ public CsrfTokenBean loadToken(HttpServletRequest request) {  HttpSession session = request.getSession(false);  if (session == null) {   return null;  }  return (CsrfTokenBean) session.getAttribute(this.sessionAttributeName); } /*  * (non-Javadoc)  *  * @see org.springframework.security.web.csrf.CsrfTokenRepository#generateToken(javax.  * servlet .http.HttpServletRequest)  */ public CsrfTokenBean generateToken(HttpServletRequest request) {  return new CsrfTokenBean(this.headerName, this.parameterName,    createNewToken()); } private String createNewToken() {  return UUID.randomUUID().toString(); } @Override public void cacheUrl(HttpServletRequest request, HttpServletResponse response) {  String queryString = request.getQueryString();  // 被拦截前的请求URL  String redirectUrl = request.getRequestURI();  if (StringUtils.isNotBlank(queryString)) {   redirectUrl = redirectUrl.concat("?").concat(queryString);  }  HttpSession session = request.getSession();  session.setAttribute(this.cacheUrlAttributeName, redirectUrl); } @Override public String getRemoveCacheUrl(HttpServletRequest request, HttpServletResponse response) {  HttpSession session = request.getSession(false);  if (session == null) {   return null;  }  String redirectUrl = (String) session.getAttribute(this.cacheUrlAttributeName);  if (StringUtils.isBlank(redirectUrl)) {   return null;  }  session.removeAttribute(this.cacheUrlAttributeName);  return redirectUrl; }}

关于利用spring security防御csrf攻击的原理是什么就分享到这里了,希望以上内容可以对大家有一定的帮助,可以学到更多知识。如果觉得文章不错,可以把它分享出去让更多的人看到。

0