zoukankan      html  css  js  c++  java
  • 跨站请求伪造 CSRF / XSRF<二:应用>

    防御的方法主要有两种<java示例>

    1.检查Referer字段

      HTTP头中有一个Referer字段,这个字段用以标明请求来源于哪个地址。在处理敏感数据请求时,通常来说,Referer字段应和请求的地址位于同一域名下。以上文银行操作为例,Referer字段地址通常应该是转账按钮所在的网页地址,应该也位于www.examplebank.com之下。而如果是CSRF攻击传来的请求,Referer字段会是包含恶意网址的地址,不会位于www.examplebank.com之下,这时候服务器就能识别出恶意的访问。

      这种办法简单易行,工作量低,仅需要在关键访问处增加一步校验。

      ***但这种办法也有其局限性,因其完全依赖浏览器发送正确的Referer字段。虽然http协议对此字段的内容有明确的规定,但并无法保证来访的浏览器的具体实现,亦无法保证浏览器没有安全漏洞影响到此字段。并且也存在攻击者攻击某些浏览器,篡改其Referer字段的可能。

    检查Referer字段如下:

        @Override
        public void doFilter(ServletRequest servletrequest,
                ServletResponse servletresponse, FilterChain filterchain)
                throws IOException, ServletException {
            
            HttpServletRequest httpReq = (HttpServletRequest)servletrequest;
            HttpServletResponse httpResp = (HttpServletResponse)servletresponse;
            String path = httpReq.getContextPath();  
            String basePath = servletrequest.getScheme() + "://" + servletrequest.getServerName()  
                    + ":" + servletrequest.getServerPort() + path + "/";
            
            // 防CSRF 之:HTTP 头设置 Referer过滤  
            String referer = httpReq.getHeader("Referer"); // REFRESH
            if (referer != null && referer.indexOf(basePath) < 0) { //*** 
                //判断为不安全的访问
                String ctxPath = httpReq.getContextPath(); 
                httpResp.sendRedirect(ctxPath+"/common/goNoSecurity.do");
                return; 
            } 
            filterchain.doFilter(servletrequest, httpResp);
        }

    2.添加校验token

      由于CSRF的本质在于攻击者欺骗用户去访问自己设置的地址,所以如果要求在访问敏感数据请求时,要求用户浏览器提供不保存在cookie中,并且攻击者无法伪造的数据作为校验,那么攻击者就无法再执行CSRF攻击。这种数据通常是表单中的一个数据项。服务器将其生成并附加在表单中,其内容是一个伪乱数。当客户端通过表单提交请求时,这个伪乱数也一并提交上去以供校验。正常的访问时,客户端浏览器能够正确得到并传回这个伪乱数,而通过CSRF传来的欺骗性攻击中,攻击者无从事先得知这个伪乱数的值,服务器端就会因为校验token的值为空或者错误,拒绝这个可疑请求。

      token的校验规则:

      a. 账户登录时产生一个token字段,且将token传入session中;

      b.前端发送请求时,利用jsp中的session对象获取到token,一并传到后台;

      c.在过滤器中校验请求中的token参数和session中的token参数是否一致;

      d.如果一致,则通过;不一致则跳转到不安全页面。

    代码如下:

      a. 账户登录时产生一个token字段,且将token传入session中;

        private void mainLoginDirect(HttpServletRequest request,Map<String, Object> respMap, Integer isLogin, RegAccout account) {
            //salt加密
            RegSalt rs=regAccoutService.getRegSaltbyAccname(account.getAcc_name());
            if (rs==null) {
                respMap.put(Const.AJAX_SERVICE_MESSAGE, "无效用户!");
            }else{
                String password=account.getPassword();
                String password1=request.getParameter("password");
                String isVip=request.getParameter("isVip");
                String salt=rs.getSalt();
                if(CryptoUtils.verify(password, password1, salt)){//密码正确                
                    SessionUtil.setAttribute(Const.SESSION_CSRFTOKEN,SRMStringUtil.getUUID());//***创建token,防CSRF
                    respMap.put("pw", true);
                }else{//密码不正确
                    respMap.put(Const.AJAX_SERVICE_MESSAGE, "密码错误!请重新输入。");
                    respMap.put("pw", false);
                }
            }
        }

      b.前端发送请求时,利用jsp中的session对象获取到token,一并传到后台;

    function asyncAjaxMethod(url,params,isasync,fn){
        params.csrftoken ="${sessionScope.csrftoken }";//*** 这里获取token
        $.ajax({
               type: "POST",
               async:isasync,
               url: getwebroot()+url,
               dataType:'json',
               data:params,//***
               beforeSend: function () {
                   $("body").append("<div id='spin_wrap'></div>");
                   $("#spin_wrap").addClass("spin_mask");
                spinner.spin(document.getElementById("spin_wrap"));
               },
               success:function(result){
                       //关闭loding效果
                       spinner.spin();
                       $("body #spin_wrap").remove();
                    if(result.success==false){
                        //人为抛出异常,但是要求设置success=false
                        if(result.ajaxErrorCode==999){
                            if(hasRemainLoginPop()){//有弹出过登录框
                                remindLogin_flag=true;
                            }
                            if(!remindLogin_flag){
                                window.wxc.xcConfirm("登录超时,请重新登录", window.wxc.xcConfirm.typeEnum.confirm,
                                {
                                    onClose:function(){//超时,则弹出登录框
                                        window.plugLogin();
                                    }
                                });
                            }
                        }else if(result.ajaxErrorCode==970){
                            go_redirect("error/noSecurity.htm");
                        }else if(result.ajaxErrorCode==980){
                            go_redirect("error/beKick.html");
                        }else if( result.ajaxErrorCode==300)
                        {
                            var option ={title:"提示",btn:parseInt("0001",2)};
                            window.wxc.xcConfirm(result.message, window.wxc.xcConfirm.typeEnum.custom,option);
                        }
                        else
                        {
                            //300:请求参数异常
                            window.wxc.xcConfirm("操作失败,请稍后再试", window.wxc.xcConfirm.typeEnum.error);
                        }
                            
                    }else{//默认success==true
                        if(result.ajaxErrorCode==200){
                            if(fn!=null && typeof(fn)=="function"){
                                fn(result);
                            }
                        }else{
                            window.wxc.xcConfirm('抱歉,返回标志错误,请稍后再试或与管理员联系', window.wxc.xcConfirm.typeEnum.error);
                        }
                    }
               },
               //异常未捕获时,跳转到这里
               error: function (XMLHttpRequest, textStatus, errorThrown) {
                    //关闭loding效果
                       spinner.spin();
                       $("body #spin_wrap").remove();
                       if(XMLHttpRequest.status==404){
                           go_redirect("error/404.html");
                       }else if(XMLHttpRequest.status==500){
                           go_redirect("error/500.html");
                       }else{
                           window.wxc.xcConfirm('抱歉,程序异常未捕获,请稍后再试或与管理员联系', window.wxc.xcConfirm.typeEnum.error);
                       }
               }
            });
        return remindLogin_flag;
    }

      c.在过滤器中校验请求中的token参数和session中的token参数是否一致;

      d.如果一致,则通过;不一致则跳转到不安全页面。

    if(tarUri.endsWith(".do") && !isWithoutUri(tarUri) && httpReq.getContentType() != null){
                                    String contentType = httpReq.getContentType();
                                    String post_csrftoken = "";
                                    if(contentType != null && contentType.contains("multipart/form-data")){
                                        MultipartHttpServletRequest multiReq = multipartResolver.resolveMultipart(httpReq);
                                        post_csrftoken=multiReq.getParameter(Const.SESSION_CSRFTOKEN);
                                        req = multiReq;
                                    }else{
                                        post_csrftoken=httpReq.getParameter(Const.SESSION_CSRFTOKEN);
                                    }
                                    //csrf防御:判断是否带token
                                    String csrftoken=(String)SessionUtil.getAttribute(Const.SESSION_CSRFTOKEN);
                                    if(post_csrftoken==null || !csrftoken.equals(post_csrftoken)){//***判断token正确性
                                        //判断为不安全的访问
                                        httpResp.sendRedirect(ctxPath+"/common/goNoSecurity.do");
                                        return;
                                    }
                                }
  • 相关阅读:
    vue中根据手机类型判断是安卓、微信或IOS跳转相应下载页面
    冒泡排序、数组去重
    vue2中component父子组件传递数据props的使用
    filter 在CSS用的效果
    纪念一下——做事要踏实
    要先学会做人,再做事
    2014再见,2013你好!
    --initialize specified but the data directory has files in it. Aborting. 解决
    Unity 发生 System.TypeInitializationException: “”的类型初始值设定项引发异常。的错误
    C#和delphi交互传结构体的delphi接收不到问题
  • 原文地址:https://www.cnblogs.com/springlight/p/6229635.html
Copyright © 2011-2022 走看看