SQL、JS脚本 防注入攻击过滤器

1. web.xml 配置过滤器

 <!-- SQL、JS脚本 注入攻击过滤器 -->
    <filter>
	   <filter-name>xssFilter</filter-name>
	   <filter-class>com.huawei.esysadmin.common.filter.XssFilter</filter-class>
    </filter>
    <filter-mapping>
	   <filter-name>xssFilter</filter-name>
	   <url-pattern>/*</url-pattern>
    </filter-mapping>

 

2. XssFilter 代码实现

public class XssFilter extends BaseFilter implements Filter
{
    private static final Log logger = LogFactory.getLog(XssFilter.class);
    
    /**
     * 必须要过滤的URL
     */
    private String[] mustFilterUrlList = null;
    
    /**
     * 不进行过滤的URL
     */
    private String[] notFilterUrlList = null;
    
    /**
     * 需要过滤的关键词
     */
    private String[] filterWordList = null;
    
    /**
     * 包含非法单词,但是整体合法的单词
     */
    private String[] ignorWordList = null;
    
    /**
     * 过滤器入口方法
     * 
     */
    public void doFilter(ServletRequest arg0, ServletResponse arg1, FilterChain arg2) throws IOException, ServletException
    {
        HttpServletRequest request = (HttpServletRequest)arg0;
        HttpServletResponse response = (HttpServletResponse)arg1;
        String url = request.getRequestURI();
        
        if (this.ignoreUrlFromWeb(url.replaceAll(request.getContextPath(), "")))
        {
            arg2.doFilter(request, response);
            return;
        }
        
        // 检查url是否必须要过滤掉
        if (this.isUrlMustFilter(url))
        {
            logger.info("XssFilter filtered invalid url \"" + url + "\", and will response 404 to client.");
            response.setStatus(404);
            return;
        }
        
        // 检查url是否不需要过滤
        if (this.isUrlNotFilter(url))
        {
            // 如果不需要过滤,则执行下一个过滤器
            arg2.doFilter(arg0, arg1);
            return;
        }
        
        //校验queryString
        String queryString = request.getQueryString();
        if (containsIllegalWord(queryString))
        {
            logger.info("XssFilter filtered invalid queryString \"" + queryString + "\", and will redirect to error page.");
            
            throw new ESysAdminException("invalid request");
        }
        
        // 解析请求参数,并进行参数过滤
        Enumeration<String> params = request.getParameterNames();
        if (null != params)
        {
            // 参数名称
            String paramName = null;
            // 参数值
            String[] paramValues = null;
            
            while (params.hasMoreElements())
            {
                paramName = params.nextElement();
                paramValues = request.getParameterValues(paramName);
                
                if (null == paramValues)
                {
                    continue;
                }
                
                for (String value : paramValues)
                {
                    if (this.containsIllegalWord(value))
                    {
                        logger.info("XssFilter filtered invalid input param \"" + paramName + "\" with values \"" + value + "\", and will redirect to error page.");
                        
                        throw new ESysAdminException("invalid request");
                    }
                }                
            }
        }
        
        String type = request.getContentType();
        // 文件上传类型采用的contenttype为multipart/form-data;类型,上传的是文件流,无须校验
        // 如果是普通的form表单提交的,contenttype是非json模式的,不走json获取的参数校验
        if (StringUtils.isBlank(type) || !type.startsWith("application/json"))
        {
            arg2.doFilter(request, arg1);
            return;
        }
        
        ServletRequest requestWrapper = null;
        if (request instanceof HttpServletRequest)
        {
            requestWrapper = new ReaderReuseHttpServletRequestWrapper(request);
        }
        Reader reader = requestWrapper.getReader();
        
        // 读取Request Payload数据  axios的请求类型
        String payload = IOUtils.toString(reader);
        
        if (StringUtils.isNotBlank(type) && type.startsWith("application/json"))
        {
            parseJSONString(payload,request,response);
        }
        else
        {
            // 解析请求参数,并进行参数过滤
            Enumeration<String> params1 = request.getParameterNames();
            if (null != params1)
            {
                // 参数名称
                String paramName = null;
                // 参数值
                String[] paramValues = null;
                
                while (params1.hasMoreElements())
                {
                    paramName = params1.nextElement();
                    paramValues = request.getParameterValues(paramName);
                    
                    if (null == paramValues)
                    {
                        continue;
                    }
                    
                    for (String value : paramValues)
                    {
                        if (this.containsIllegalWord(value))
                        {
                            logger.error("XssFilter filtered invalid input param \"" + paramName + "\" with values \""
                                    + value + "\", and will redirect to error page.");
                            
                            throw new ESysAdminException("invalid request");
                        }
                    }
                }
            }
        }
        arg2.doFilter(requestWrapper, arg1);
    }
    
    /**
     * 判断url是否必须过滤
     * 
     * @param url
     * @return
     * @see [类、类#方法、类#成员]
     */
    private boolean isUrlMustFilter(String url)
    {
        // 查看参数表中是否存在需要过滤的url配置
        //String[] mustFilterUrls = getXssFilterConfigParam("BBCYYGLPT_XSSFILTER_MUSTFILTERURL");
        String[] mustFilterUrls = null;
        
        ConfBlackWhiteListBean mustFilterUrlsConfig = ParamDictUtil.queryBlackWhiteList("black", "BBCYYGLPT_XSSFILTER_MUSTFILTERURL");
        
        if (null != mustFilterUrlsConfig)
        {
            List<String> mustFilterList = mustFilterUrlsConfig.getBlackWhiteList();
            if (null != mustFilterList && !mustFilterList.isEmpty())
            {
                mustFilterUrls = mustFilterList.stream()
                        .map(word -> word.toLowerCase(Locale.US))
                        .collect(Collectors.toList())
                        .toArray(new String[mustFilterList.size()]);
            }
            
        }
        
        if (null != mustFilterUrls)
        {
            mustFilterUrlList = mustFilterUrls;
        }
        
        // url为空或未配置必须过滤的url,则不是必须过滤的url
        if (StringUtils.isBlank(url) || null == mustFilterUrlList || 0 == mustFilterUrlList.length)
        {
            return false;
        }
        
        // 转换为小写字符
        url = url.toLowerCase(Locale.US);
        
        for (String must : mustFilterUrlList)
        {
            if (url.contains(must))
            {
                return true;
            }
        }
        
        return false;
    }
    
    /**
     * 判断url是否不需要过滤
     * 
     * @param url
     * @return
     * @see [类、类#方法、类#成员]
     */
    private boolean isUrlNotFilter(String url)
    {
        // url为空,则不需要过滤
        if (StringUtils.isBlank(url))
        {
            return true;
        }
        
        // 转换为小写字符
        url = url.toLowerCase(Locale.US);
        
        // 查看参数表中是否存在不需要过滤的url配置
        //String[] notFilterUrls = getXssFilterConfigParam("BBCYYGLPT_XSSFILTER_NOTFILTERURL");
        String[] notFilterUrls = null;
        ConfBlackWhiteListBean queryBlackWhiteListConfig = ParamDictUtil.queryBlackWhiteList("white",
                "BBCYYGLPT_XSSFILTER_NOTFILTERURL");
        if (null != queryBlackWhiteListConfig)
        {
            List<String> blackWhiteList = queryBlackWhiteListConfig.getBlackWhiteList();
            if (null != blackWhiteList && !blackWhiteList.isEmpty())
            {
                notFilterUrls = blackWhiteList.stream()
                        .map(urlWhite -> urlWhite.toLowerCase(Locale.US))
                        .collect(Collectors.toList())
                        .toArray(new String[blackWhiteList.size()]);
            }
            
        }
        
        
        
        if (null != notFilterUrls)
        {
            notFilterUrlList = notFilterUrls;
        }
        
        // 判断url是否不需要过滤
        if (null != notFilterUrlList)
        {
            for (String not : notFilterUrlList)
            {
                if (url.contains(not))
                {
                    return true;
                }
            }
        }
        
        return false;
    }
    
 
    /**
     * 判断字符串是否包含非法关键字
     * 
     * @param s
     * @return
     * @see [类、类#方法、类#成员]
     */
    private boolean containsIllegalWord(String s)
    {
        // 参数值为空,默认合法
        if (StringUtils.isBlank(s))
        {
            return false;
        }
        
        // 转换为小写模式
        s = s.toLowerCase(Locale.US);
        
        // 查看参数表中是否存在忽略关键词配置
        //String[] ignorWords = getXssFilterConfigParam("BBCYYGLPT_XSSFILTER_IGNORWORDS");
        String[] ignorWords = null;
        ConfBlackWhiteListBean ignorWordsConfig = ParamDictUtil.queryBlackWhiteList("white",
                "BBCYYGLPT_XSSFILTER_IGNORWORDS");
        if (null != ignorWordsConfig)
        {
            List<String> ignorWordWhiteList = ignorWordsConfig.getBlackWhiteList();
            if (null != ignorWordWhiteList && !ignorWordWhiteList.isEmpty())
            {
                ignorWords = ignorWordWhiteList.stream()
                        .map(ignorWord -> ignorWord.toLowerCase(Locale.US))
                        .collect(Collectors.toList())
                        .toArray(new String[ignorWordWhiteList.size()]);
            }
            
        }
        
        
        if (null != ignorWords)
        {
            ignorWordList = ignorWords;
        }
        
        // 排除忽略关键词
        if (null != ignorWordList)
        {
            for (String ignor : ignorWordList)
            {
                s = s.replaceAll(ignor, "");
            }
        }
        
        // 查看参数表中是否存在非法关键词配置
        //String[] filterWords = getXssFilterConfigParam("BBCYYGLPT_XSSFILTER_FILTERWORDS");
        String[] filterWords = null;
        ConfBlackWhiteListBean filterWordsConfig = ParamDictUtil.queryBlackWhiteList("black", "BBCYYGLPT_XSSFILTER_FILTERWORDS");
        if (null != filterWordsConfig)
        {
            List<String> result = filterWordsConfig.getBlackWhiteList();
            if (null != result && !result.isEmpty())
            {
                filterWords = result.stream()
                        .map(word -> word.toLowerCase(Locale.US))
                        .collect(Collectors.toList())
                        .toArray(new String[result.size()]);
            }
        }
        
        if (null != filterWords)
        {
            filterWordList = filterWords;
        }
        
        // 过滤非法关键词
        if (null != filterWordList)
        {
            for (String filter : filterWordList)
            {
                if (s.contains(filter))
                {
                    return true;
                }
            }
        }
        //ParamBean config = CacheUtil.getParameterBean("BBCYYGLPT_XSSFILTER_REGEXP", Constants.PARAM_REGION_ALL);
        ConfParameterBean config = ParamDictUtil.queryParameterById(ParamDictConstants.ParamType.RULE, "BBCYYGLPT_XSSFILTER_REGEXP", ParamDictConstants.BusiType.SYSTEM);
        if (null != config && StringUtils.isNotBlank(config.getParamValue()))
        {
            Pattern pattern = Pattern.compile(config.getParamValue());
            if (pattern.matcher(s).find())
            {
                return true;
            }
        }
        return false;
    }
    
    /**
     * 从参数表中读取XssFilter的参数配置
     * 
     * @param paramid
     * @return
     * @see [类、类#方法、类#成员]
     */
    private String[] getXssFilterConfigParam(String paramid)
    {
        ParamBean config = CacheUtil.getParameterBean(paramid, Constants.PARAM_REGION_ALL);
        
        if (null != config && null != config.getParavalue())
        {
            String[] words = config.getParavalue().replaceAll(",", ",").toLowerCase(Locale.US).split(",");
            
            if (words.length > 0)
            {
                return words;
            }
        }
         return null;
    }
    

    /**
     * 两个方法都注明方法只能被调用一次,由于RequestBody是流的形式读取, 那么流读了一次就没有了,所以只能被调用一次。 既然是因为流只能读一次的原因,那么只要将流的内容保存下来,就可以实现反复读取了
     * 
     */
    public static class ReaderReuseHttpServletRequestWrapper extends HttpServletRequestWrapper
    {
        
        private final byte[] body;
        
        public ReaderReuseHttpServletRequestWrapper(HttpServletRequest request) throws IOException
        {
            super(request);
            body = IOUtils.toString(request.getReader()).getBytes(Charset.forName("UTF-8"));
        }
        
        @Override
        public BufferedReader getReader() throws IOException
        {
            return new BufferedReader(new InputStreamReader(getInputStream()));
        }
        
        @Override
        public ServletInputStream getInputStream() throws IOException
        {
            final ByteArrayInputStream bais = new ByteArrayInputStream(body);
            return new ServletInputStream()
            {
                
                @Override
                public int read() throws IOException
                {
                    return bais.read();
                }
                
            };
        }
    }
    
    public Object parseJSONString(String jsonString,HttpServletRequest request,HttpServletResponse response)
    {
        JSONArray jsonArray = null;
        JSONObject jsonObj = null;
        if (jsonString.startsWith("["))
        {
            jsonArray = JSONArray.parseArray(jsonString);
            return parseJSONArr(jsonArray,request,response);
        }
        else
        {
            jsonObj = JSONObject.parseObject(jsonString);
            return parseJSONObj(jsonObj,request,response);
        }
        
    }
    
    public Map<String, Object> parseJSONObj(JSONObject jsonObj,HttpServletRequest request,HttpServletResponse response)
    {
        Map<String, Object> paramMap = new HashMap<String, Object>();
        if (null == jsonObj)
        {
            return paramMap;
        }
        String val = "";
        Object obj = null;
        for (Map.Entry<String, Object> entry : jsonObj.entrySet())
        {
            val = JSONObject.toJSONString(entry.getValue());
            if (val.startsWith("{") || val.startsWith("["))
            {
                obj = parseJSONString(val,request,response);
                paramMap.put(entry.getKey(), obj);
            }
            else
            {
                if (containsIllegalWord(String.valueOf(entry.getValue())))
                {
                    logger.info("XssFilter filtered invalid input param \"" + entry.getKey() + "\" with values \""
                            + entry.getValue() + "\", and will redirect to error page.");
                    
                    throw new ESysAdminException("invalid request");
                }
                paramMap.put(entry.getKey(), entry.getValue());
            }
        }
        return paramMap;
    }
    
    public List<Object> parseJSONArr(JSONArray jsonArray,HttpServletRequest request,HttpServletResponse response)
    {
        List<Object> paramList = new ArrayList<Object>();
        if (null == jsonArray)
        {
            return paramList;
        }
        
        String val = "";
        Object obj = null;
        Iterator<Object> iterator = jsonArray.iterator();
        while (iterator.hasNext())
        {
            obj = iterator.next();
            val = JSONObject.toJSONString(obj);
            if (val.startsWith("{") || val.startsWith("["))
            {
                paramList.add(parseJSONString(val,request,response));
            }
            else
            {
                if (containsIllegalWord(String.valueOf(obj)))
                {
                    logger.error("XssFilter filtered invalid input param with values \"" + obj
                            + "\", and will redirect to error page.");
                    throw new ESysAdminException("invalid request");
                }
                paramList.add(obj);
            }
        }
        
        return paramList;
    }
 
    
    /**
     * 初始化过滤器
     * @param arg0
     * @throws ServletException
     */
    public void init(FilterConfig arg0) throws ServletException
    {
        // 解析必须过滤的url
        String must = arg0.getInitParameter("mustFilterUrlList");
        if (StringUtils.isNotBlank(must))
        {
            // 转换中文逗号,并且统一转换为小写字符
            must = must.toLowerCase(Locale.US);
            mustFilterUrlList = must.split(",");
            
            logger.info("init XssFilter with must filter url:" + must);
        }
        
        // 解析不需要过滤的url
        String not = arg0.getInitParameter("notFilterUrlList");
        if (StringUtils.isNotBlank(not))
        {
            not = not.toLowerCase(Locale.US);
            notFilterUrlList = not.split(",");
            
            logger.info("init XssFilter with not filter url:" + not);
        }
        
        // 解析需要过滤关键词配置
        String filterWords = arg0.getInitParameter("filterWords");
        if (StringUtils.isNotBlank(filterWords))
        {
            filterWords = filterWords.trim().toLowerCase(Locale.US);
            filterWordList = filterWords.split(",");
            
            logger.info("init XssFilter with filter words:" + filterWords);
        }
        
        // 解析忽略过滤规则(安全的)关键词配置
        String ignorWords = arg0.getInitParameter("ignorWords");
        if (StringUtils.isNotBlank(ignorWords))
        {
            ignorWords = ignorWords.trim().toLowerCase(Locale.US);
            ignorWordList = ignorWords.split(",");
            
            logger.info("init XssFilter with ignor words:" + ignorWords);
        }
    }
    
    /**
     * 销毁过滤器
     */
    public void destroy()
    {
        this.mustFilterUrlList = null;
        this.notFilterUrlList = null;
        this.filterWordList = null;
        this.ignorWordList = null;
    }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值