AppScan漏洞扫描问题与及解决方案

       前言:

  在项目验收阶段,客户或多或少会提一些系统有关的性能、安全等指标的检测报告,这不,最近就有个项目验收前客户指明需要有个系统的出厂安全测试报告(这个“出厂安全测试报告”只是客户的口头俗称,其实安全的范围比较广,专业术语定义为:WEB应用漏洞扫描、主机扫描、数据库扫描、源代码漏洞扫描等等 ),我们找了几家专门做这种安全测试的第三方机构(有资质出安全测试报告证书的公司)进行咨询与报价(当然了,同等条件下,价位越低的最好,公司能省则省嘛),第三方机构估计也是接过很多这种要求的单子,其实他们一听到这个要求就知道我们基本要的是啥标准的报告(大部分情况下都是WEB应用漏洞扫描)。

  与筛选的最优的合作机构洽谈好合同等事宜之后,就进行配合相关的测试准备工作(基本也就是部署好项目到公司服务器上,为这个系统开放一个公网端口出去,并且把系统访问地址和一个被测试的登录用户与密码通知给第三方机构,让他们进行测试),顺便一提,我们是选择远程测试的方式,测试方式其实有现场和远程,由于广州最近疫情复发,挺严重的,公司就考虑远程测试(其实第三方测试机构这时候也不愿来现场测的,哈哈哈)。

  听我废话了这么多的小伙伴,请原谅我的唠叨,哈哈哈,在此说声抱歉!!!!

  正题:

  第三方测试机构通过AppScan软件扫出来我们系统的很多漏洞问题(主要是开发时根本没考虑过这方面的测试,所以代码逻辑方面很多地方是写的不够严谨的,当然也是像这种专业的扫描工具才能扫出这些问题,我们通常的测试手段基本发现不了这些问题),一开始看到问题文档还是很头大,主要还是之前没干过这玩意,不知道怎么去修复,好在谷歌/百度了之后,知道可以给系统写个拦截器(绝大部分的问题我都是通过拦截器处理掉的,所以我着重讲拦截器方面的解决方案),拦截系统的相关请求(我是配置了拦截所有请求),过滤xss攻击(例如相关的特殊字符等处理)

  (1)、扫描结果如下图:

(2)、解决方案--拦截器

  一、写一个实现Filter 的类(名称自取,我的就叫XssFilter),重现这个doFilter(ServletRequest request, ServletResponse response, FilterChain chain),应为咱们所有的请求拦截都是进过这个方法进行相关处理的。

代码如下所示:

package com.ljw.biz.filterUtil;

import java.io.IOException;
import java.util.Enumeration;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;public class XssFilter implements Filter {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        // TODO Auto-generated method stub
    }
    
    @SuppressWarnings("static-access")
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        /*XssHttpServletRequestWraper xssRequest = new XssHttpServletRequestWraper((HttpServletRequest) request);
        chain.doFilter(xssRequest, response);*/        
        
        /*HttpServletRequest _request = (HttpServletRequest) request;
        String path = _request.getServletPath();        
        System.out.println("path===========================" + path);*/                
        
        // 防止流读取一次后就没有了, 所以需要将流继续写出去
        /*HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        HttpServletRequest req= new XssHttpServletRequestWraper(httpServletRequest);*/

        HttpServletRequest req= (HttpServletRequest)  request;
        //HttpServletResponse res=(HttpServletResponse)response;
        
        //获得所有请求参数名    
        Enumeration params = req.getParameterNames();    
        
        String sql = "";
        
        boolean requestCheck = true;        

        //防止sql注入
        while (params.hasMoreElements()) {
            //得到参数名
            String name = params.nextElement().toString();
            
            //-System.out.println("name===========================" + name);
            if("systemType".equals(name)) {
                //-System.out.println("存在cookie伪造登录系统!");
                requestCheck = false;
            }
            
            //得到参数对应值
            String[] value = req.getParameterValues(name);
            for (int i = 0; i < value.length; i++) {
                //-System.out.println("value===========================" + value[i]);
                sql = sql + value[i];
            }
        }

        //-System.out.println("全部参数值:"+sql);
        
        String noCheck_sql = "com.shd.biz.bureauDetect.testset.service.TestSetServiceinsertPdTestPoint{";
        if(sql.indexOf(noCheck_sql) == -1) {
            if (this.isSQLOrScript(sql)) {
                //-System.out.println("传入的参数存在非法字符!");
                requestCheck = false; //false:表示参数存在非法字符,true:表示参数正常
            }
        }
        
        //防止跨站点伪造csrf攻击
        String referer = req.getHeader("Referer"); 
        //-System.out.println("Referer : " + referer);
        if(referer != null){
            /*if(referer.indexOf("jsessionid") != -1) {
                System.out.println("存在跨站点伪造csrf攻击!");
                requestCheck = false;
            }*/
            if(referer.indexOf("bogus.referer") != -1) {
                //-System.out.println("存在跨站点伪造csrf攻击!");
                requestCheck = false;//false:表示请求存在csrf攻击,true:表示正常请求
            }
        }
        
        //防止通过 Bash 进行远程命令执行
        String bash = req.getHeader("Accept");
        //System.out.println("Accept : " + bash);
        if(bash != null && (this.isBashAttack(bash))){
            //-System.out.println("存在Bash进行远程命令执行!");
            requestCheck = false;//false:表示请求存在 Bash远程命令攻击,true:表示正常请求
        }
        
        //防止cookie伪造
        String contentType = req.getHeader("Content-Type"); 
        //System.out.println("Content-Type : " + contentType);
        if(contentType != null && (this.isCookieFalsify(contentType))){
            //-System.out.println("存在Cookie伪造访问!");
            requestCheck = false;//false:表示请求存在cookie伪造,true:表示正常请求
        }
        
        //防止cookie伪造
        Cookie[] cookieContent = req.getCookies();
        if(cookieContent != null && cookieContent.length >0) {
            for (Cookie cookie : cookieContent) {
                /*System.out.println("cookie长度:" + cookie.getValue().length());
                System.out.println("cookieID : " + cookie.getName() + "  cookieValue:" + cookie.getValue());*/
                if(!"JSESSIONID".equals(cookie.getName()) && !"_ga".equals(cookie.getName())){
                    requestCheck = false;
                }
                if("JSESSIONID".equals(cookie.getName())) {
                    //-System.out.println("进来的是:"+cookie.getName());
                    if(cookie.getValue() == null || "".equals(cookie.getValue()) || !this.strValidate(cookie.getValue())) {
                        //-System.out.println("存在跨站点伪造csrf攻击!");
                        requestCheck = false;
                    }
                }                
            }
        }else {
            //-System.out.println("cookie为空, 存在跨站点伪造csrf攻击!");
            requestCheck = false;
        }
                
        if(requestCheck == true) {
            chain.doFilter(request, response);
        }
    }

    @Override
    public void destroy() {
        // TODO Auto-generated method stub
    }
    
    private static boolean isSQLOrScript(String str){

        str = str.toLowerCase();//统一转为小写

        String badStr = "net user|xp_cmdshell|/add|exec master.dbo.xp_cmdshell|onclick|onfocus|new function|window|==|" +

        "net localgroup administrators|count|asc|mid|having|alert|<|>|img src|.net|.html|.source|.xml|" +

        "delete from|drop table|truncate|onmouseover|wfxssprobe|wf_xsrf|wfxss|aaaaaaaaaaaaaa|" +

        "from|%|JavaScript|script|ping|+|'|\'|\"|\(|\)|
|
|"|" +
        "didweather梅州|systemtype|probephishing|main"|select 1|";

        String inj_stra[] = badStr.split("\|");

        for (int i=0 ; i < inj_stra.length ; i++ ) {
            if (str.indexOf(inj_stra[i])>=0){
                //-System.out.println("特殊字符:"+inj_stra[i]);
                return true;
            }
        }
        return false;
    }
    
    //校验是否bash远程命令攻击
    private static boolean isBashAttack(String str){

        str = str.toLowerCase();//统一转为小写

        String badStr = "shellshock|echo|\(|\)|\{|\}|\:|\;|";

        String inj_stra[] = badStr.split("\|");

        for (int i=0 ; i < inj_stra.length ; i++ ) {
            if (str.indexOf(inj_stra[i])>=0){
                return true;
            }
        }
        return false;
    }
    
    //校验是否Cookie伪造访问
    @SuppressWarnings("unused")
    private static boolean isCookieFalsify(String str){

        str = str.toLowerCase();//统一转为小写

        String badStr = "opensymphony|xwork2|appscanheader|appscanvalue|\(|\)|\{|\}|";

        String inj_stra[] = badStr.split("\|");

        for (int i=0 ; i < inj_stra.length ; i++ ) {
            if (str.indexOf(inj_stra[i])>=0){
                return true;
            }
        }
        return false;
    }
    
    /**
      * 
      * @function 验证字符串仅包含字符和数字,并且字符长度为32位 
      * @author Liangjw
      * @date 2021年6月2日上午10:16:09  
      * @param cookieValue
      * @return
      * boolean
     */
    private static boolean strValidate(String cookieValue){
        String LETTER_DIGIT_REGEX = "^(?=.*[0-9])(?=.*[a-zA-Z])([a-zA-Z0-9]{32})$";
        return cookieValue.matches(LETTER_DIGIT_REGEX);
    }

}

上面的代码中的isSQLOrScript(String str)方法,里面特殊字符处理,要根据自己项目的具体情况来定,不要盲从,因为我这里过滤的都是经过我们项目中用不到的特殊字符,所以过滤掉,防止xss攻击利用到这些特殊字符!如下图所示:

当然,别忘了在web.xml配置文件中配置一下这个拦截器,不然你写了也不起作用!!!,配置很简单,如下图所示:

 搞定,,,其实挺简单的~

 温馨小提示: 如果存在一些比较尴尬的特殊字符攻击,例如项目中有些地方自己本来就使用到特殊字符进行前后端数据传递,但是如果咱们拦截了这个特殊字符,那么就影响了咱们系统上某些功能的正常使用问题;所以,像这种情况时建议:1.要么修改自己之前的方法,前端请求时将参数中的特殊字符以某种方式替换,又或者在后台进行数据拼接,将特殊字符拼上再进行后台处理(但是第1种建议有时候比较麻烦,可能牵扯的地方有点多并不好解决问题);2.在拦截器阶段,把特殊的请求放开,对它不进行拦截过滤处理,其余的请求则依旧进行拦截过滤。如下图所示,不拦截处理特殊的请求:

原文地址:https://www.cnblogs.com/4AMLJW/p/appscan_loudong20210605135255.html