zoukankan      html  css  js  c++  java
  • CSRF spring mvc 跨站请求伪造防御(转)

    CSRF 
    CSRF(Cross-site request forgery跨站请求伪造,也被称为“One Click Attack”或者Session Riding,通常缩写为CSRF或者XSRF,是一种对网站的恶意利用。尽管听起来像跨站脚本(XSS),但它与XSS非常不同,并且攻击方式几乎相左。XSS利用站点内的信任用户,而CSRF则通过伪装来自受信任用户的请求来利用受信任的网站。与XSS攻击相比,CSRF攻击往往不大流行(因此对其进行防范的资源也相当稀少)和难以防范,所以被认为比XSS更具危险性。

    攻击示例 
    如:一个网站用户Bob可能正在浏览聊天论坛,而同时另一个用户Alice也在此论坛中,并且后者刚刚发布了一个具有Bob银行链接的图片消息。设想一下,Alice编写了一个在Bob的银行站点上进行取款的form提交的链接,并将此链接作为图片src。如果Bob的银行在cookie中保存他的授权信息,并且此cookie没有过期,那么当Bob的浏览器尝试装载图片时将提交这个取款form和他的cookie,这样在没经Bob同意的情况下便授权了这次事务。 
    CSRF是一种依赖web浏览器的、被混淆过的代理人攻击(deputy attack)。在上面银行示例中的代理人是Bob的web浏览器,它被混淆后误将Bob的授权直接交给了Alice使用。 
    下面是CSRF的常见特性: 
    依靠用户标识危害网站 
    利用网站对用户标识的信任 
    欺骗用户的浏览器发送HTTP请求给目标站点 
    另外可以通过IMG标签会触发一个GET请求,可以利用它来实现CSRF攻击。

    spring mvc 框架下防御策略

    思路概要: 
    1.初始化页面时在token隐藏域。 
    2.表单提交后带入token到后台,验证token,如成功继续操否为受到攻击。 
    3.操作完之后重新生成token到页面隐藏域。 
    代码示例: 
    创建拦截器:

    /**
     * <一句话功能简述>
     * <功能详细描述>
     * 防止跨站请求伪造拦截器
     * 为每个返回的页面添加CSRFToken参数
     * @author  Tangguilin
     * @version  [版本号, 2016年3月26日]
     */
    public class AvoidCSRFInterceptor extends HandlerInterceptorAdapter
    {
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception
        {
            String url = request.getRequestURI();
            if (!url.endsWith(".do"))
            {
                return true;
            }
            HandlerMethod handlerMethod = (HandlerMethod)handler;
            Method method = handlerMethod.getMethod();
            VerifyCSRFToken annotation = method.getAnnotation(VerifyCSRFToken.class);
            if (annotation != null)
            {
                String xrq = request.getHeader("X-Requested-With");//是否为Ajax标志
                //非法的跨站请求校验
                if (annotation.verifyCSRFToken() && !verifyCSRFToken(request))
                {
                    if (StringUtil.isEmpty(xrq))
                    {
                        //form表单提交,url get方式,刷新csrftoken并跳转提示页面
                        request.getSession(false).setAttribute("CSRFToken",
                            TokenProcessor.getInstance().generateToken(request));
                        response.setContentType("application/json;charset=UTF-8");
                        PrintWriter out = response.getWriter();
                        out.print("非法请求");
                        response.flushBuffer();
                        return false;
                    }
                    else
                    {
                        //刷新CSRFToken,返回错误码,用于ajax处理,可自定义
                        BaseDataResp baseResp = new BaseDataResp();
                        String csrftoken = TokenProcessor.getInstance().generateToken(request);
                        request.getSession(false).setAttribute("CSRFToken", csrftoken); 
                        baseResp.setCode(IDiMengResultCode.SystemManager.CSRF);
                        response.setContentType("application/json;charset=UTF-8");
                        PrintWriter out = response.getWriter();
                        out.print(baseResp.toString());
                        response.flushBuffer();
                        return false;
                    }
                }
    
            }
            return true;
        }
    
        @Override
        public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
            ModelAndView modelAndView)
                throws Exception
        {
    
            String url = request.getRequestURI();
            if (!url.endsWith(".do"))
            {
                return;
            }
    
            //第一次生成token
            if (modelAndView != null)
            {
                if (request.getSession(false) == null
                    || StringUtil.isEmpty((String)request.getSession(false).getAttribute("CSRFToken")))
                {
                    request.getSession(false).setAttribute("CSRFToken",
                        TokenProcessor.getInstance().generateToken(request));
                    return;
                }
            }
            //刷新token
            HandlerMethod handlerMethod = (HandlerMethod)handler;
            Method method = handlerMethod.getMethod();
            RefreshCSRFToken refreshAnnotation = method.getAnnotation(RefreshCSRFToken.class);
            String xrq = request.getHeader("X-Requested-With");//是否为Ajax标志
            if (refreshAnnotation != null && refreshAnnotation.refreshCSRFToken() && StringUtil.isEmpty(xrq))
            {
                request.getSession(false).setAttribute("CSRFToken", TokenProcessor.getInstance().generateToken(request));
                return;
            }
            VerifyCSRFToken verifyAnnotation = method.getAnnotation(VerifyCSRFToken.class);
            if (verifyAnnotation != null)
            {
                //成功后刷新token
                if (verifyAnnotation.verifyCSRFToken())
                {
                    if (StringUtil.isEmpty(xrq))
                    {
                        request.getSession(false).setAttribute("CSRFToken",
                            TokenProcessor.getInstance().generateToken(request));
                    }
                    else
                    {
                        Map<String, String> map = new HashMap<String, String>();
                        map.put("CSRFToken", TokenProcessor.getInstance().generateToken(request));
                        response.setContentType("application/json;charset=UTF-8");
                        OutputStream out = response.getOutputStream();
                        out.write((",'csrf':" + JSONObject.toJSONString(map) + "}").getBytes("UTF-8"));
                    }
                }
            }
        }
    
        /** <一句话功能简述>
         * 处理跨站请求伪造
         * 针对需要登录后才能处理的请求,验证CSRFToken校验
         * @author  tangguilin
         * @param request
         */
        protected boolean verifyCSRFToken(HttpServletRequest request)
        {
            String requstCSRFToken = request.getHeader("CSRFToken");//请求中的CSRFToken
            if (StringUtil.isEmpty(requstCSRFToken))
            {
                return false;
            }
            String sessionCSRFToken = (String)request.getSession().getAttribute("CSRFToken");
            if (StringUtil.isEmpty(sessionCSRFToken))
            {
                return false;
            }
            return requstCSRFToken.equals(sessionCSRFToken);
        }
    }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135

    注解类:

    /**
     * 跨站请求仿照注解
     * 刷新CSRFToken
     * @author  Tangguilin
     * @version  [版本号, 2016年3月28日]
     */
    @Target({java.lang.annotation.ElementType.METHOD})
    @Retention(RetentionPolicy.RUNTIME)
    public @interface RefreshCSRFToken
    {
        /** 
         * 刷新CSRFToken
         * @author tangguilin
         * @return
         */
        public abstract boolean refreshCSRFToken();
    }
    
    /**
     * 跨站请求仿照注解
     * 
     * @author  Tangguilin
     * @version  [版本号, 2016年3月28日]
     */
    @Target({java.lang.annotation.ElementType.METHOD})
    @Retention(RetentionPolicy.RUNTIME)
    public @interface VerifyCSRFToken
    {
        /** 
         * 需要验证防跨站请求
         * @author tangguilin
         * @return
         */
        public abstract boolean verifyCSRFToken();
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    配置拦截:

       <mvc:interceptors>   
            <mvc:interceptor>  
                <mvc:mapping path="/**"/> 
                <!-- 定义在mvc:interceptor下面的表示是对特定的请求才进行拦截的 -->  
               <bean class="com.front.interceptor.AvoidCSRFInterceptor"/>  
            </mvc:interceptor>  
        </mvc:interceptors> 
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    在jsp页面中添加隐藏域

     <input type="hidden" name="CSRFToken" value="${CSRFToken }"></input>  
    • 1
    • 1

    修改公用Ajax

     ajax: function(options) {
                var datas = null;
                if (options["isAjaxForm"]) {
                    $("#" + options["formId"]).submit();
                } else {
                    if (options["serialize"]) {
                        if (options["formId"]) {
                            datas = $("#" + options["formId"]).serialize();
                        } else {
                            datas = $(document.forms[0]).serialize();
                        }
                    }
                    var defaultOptions = {
                        type: "post",
                        async: false,
                        data: datas
                    };
                    options = $.extend(defaultOptions, options);
                    var path="";
                    if(options["url"].indexOf(basePath)==-1){
                        path=basePath;
                    }
                    var headers = {};
                    headers['CSRFToken'] = $("input[name='CSRFToken']").val();
                    $.ajax({
                        type: options["type"],
                        headers: headers,
                        async: options["async"],
                        dataType: options["dataType"],
                        url: path + options["url"],
                        data: options["data"],
                        success: function(data) {
                            //登录超时刷新页面后跳转首页
                            if("2000062"==data.code){
                                window.location.reload();
                            }
                            //处理跨站请求伪造
                            if("666666" == data.code){
                                if(Dialog){
                                    Dialog.show("操作失败,请刷新页面重试","error");
                                }else{
                                    alert("操作失败,请刷新页面重试");
                                }
                                return false;
                            }
                            if (typeof(options["success"]) == "function") {
                                options["success"](data);
                            }
                        },
                        error: function(data) {
                            //CSRFToken处理
                            if(data && data.readyState && data.readyState == '4'){
                                var responseBody = data.responseText;
                                if(responseBody){
                                    responseBody = "{'retData':"+responseBody;
                                    var resJson = eval('(' + responseBody + ')');
                                    $("input[name='CSRFToken']").val(resJson.csrf.CSRFToken);
                                    if (typeof(options["success"]) == "function") {
                                        options["success"](resJson.retData);
                                    }
                                }
                                return ;
                            }
                            //登录超时跳转登录页
                            /*if(data.responseText){
                                 window.location.reload();//刷新当前页面.
                                return;
                            }*/
                            if (typeof(options["error"]) == "function") {
                                options["error"](data);
                            }else{
                                alert("请求地址:"+options["url"]+"  出现异常!请联系管理员!");
                            }
    
                        }
                    });
                }
            }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78

    使用场景 
    为需要防御的Controller加上@VerifyCSRFToken(verifyCSRFToken = true)注解

    @RefreshCSRFToken(refreshCSRFToken=true):如新打开的页面,弹出框体需要CSRFToken时使用

  • 相关阅读:
    MagicAjax简单介绍(转)
    .net中RegisterStartupScript与RegisterClientScriptBlock的区别
    改变你一生的五句话 (转)
    苏州园林年卡 办理与使用 附 苏州园林门票价格
    常见的 ASP.NET 2.0 转换问题和解决方案(转)
    .net新手须知
    AJAX开发简略 (转)
    magicajax为什么不能提交? magicajax的一个bug
    所谓:有实际开发工作经验 (转)
    解读60 70 80年代职场人
  • 原文地址:https://www.cnblogs.com/bmaker/p/5805217.html
Copyright © 2011-2022 走看看