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时使用

  • 相关阅读:
    LeetCode 252. Meeting Rooms
    LeetCode 161. One Edit Distance
    LeetCode 156. Binary Tree Upside Down
    LeetCode 173. Binary Search Tree Iterator
    LeetCode 285. Inorder Successor in BST
    LeetCode 305. Number of Islands II
    LeetCode 272. Closest Binary Search Tree Value II
    LeetCode 270. Closest Binary Search Tree Value
    LeetCode 329. Longest Increasing Path in a Matrix
    LintCode Subtree
  • 原文地址:https://www.cnblogs.com/bmaker/p/5805217.html
Copyright © 2011-2022 走看看