博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
spring-security中的csrf防御机制(跨域请求伪造)
阅读量:6711 次
发布时间:2019-06-25

本文共 11709 字,大约阅读时间需要 39 分钟。

什么是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;    }}

 

 

转载于:https://www.cnblogs.com/yaomajor/p/6168760.html

你可能感兴趣的文章
计算与推断思维 六、可视化
查看>>
8Manage装配式一体化管理如何解决集成窘境
查看>>
一文看清深圳云栖阿里云重磅产品发布
查看>>
对称加密和分组加密中的四种模式(ECB、CBC、CFB、OFB)
查看>>
关于sql语句的优化问题
查看>>
SQL Server 2016新特性:数据库级别配置
查看>>
中美贸易战升级 医疗器械行业影响大
查看>>
嵌入的iframe框架自适应宽度代码
查看>>
IPTABLES常用命令之配置生产环境IPTABLES及优化
查看>>
linux服务ssh详解
查看>>
cat命令一些不常用但很有用的参数
查看>>
双色球 脱壳加去效验
查看>>
#22 系统进程调度、at、batch、mail、crontab
查看>>
Intellij IDEA Debug调试技巧
查看>>
PHP Warning: PHP Startup: unable to load dynamic library
查看>>
metasploit(MSF)终端命令大全
查看>>
Linux下php安装Redis扩展
查看>>
管理信息系统测试方法总结(二)
查看>>
EMC与NetApp NAS对比
查看>>
linux系统的启动流程
查看>>