绝对好用的安全Filter(过滤SQL和XSS)

原创
2019/09/12 18:53
阅读数 181

最近被安保测评搞得头疼,本来网站上有XSS处理,代码也是参考网络,结果发现不好使。此处呵呵了。。。。 一般情况下百度出来的结果都是不好用的例子,虽然代码还挺全面,就是拦不住你说气人不气人。

 

下面发一个经过我实际应用好使的XSS过滤器,帮大家节省时间

/**
 * 安全的Filter(过滤SQL和XSS)
 * 
 * 
 * <!-- 解决xss & sql漏洞 -->
    <filter>
        <filter-name>SafeFilter</filter-name>
        <filter-class>cn.he.xss.HttpServletRequestSafeFilter</filter-class>
        <init-param>
            <param-name>filterXSS</param-name>
            <param-value>true</param-value>
        </init-param>
        <init-param>
            <param-name>filterSQL</param-name>
            <param-value>true</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>SafeFilter</filter-name>
        <url-pattern>/*</url-pattern>
        <dispatcher>REQUEST</dispatcher>
    </filter-mapping>
 
 *
 */
public class HttpServletRequestSafeFilter implements Filter{
    public static final String FILTER_XSS = "filterXSS";
    public static final String FILTER_SQL = "filterSQL";
    FilterConfig filterConfig = null;
 
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        this.filterConfig = filterConfig;
    }
 
    @Override
    public void destroy() {
        this.filterConfig = null;
    }
 
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        boolean filterXSS = false;
        boolean filterSQL = false;
 
        if (StringUtils.isNotEmpty(filterConfig.getInitParameter(FILTER_XSS))) {
            filterXSS = Boolean.valueOf(filterConfig.getInitParameter(FILTER_XSS));
        }
 
        if (StringUtils.isNotEmpty(filterConfig.getInitParameter(FILTER_SQL))) {
            filterSQL = Boolean.valueOf(filterConfig.getInitParameter(FILTER_SQL));
        }
 
        chain.doFilter(new HttpServletRequestSafeWrapper((HttpServletRequest) request, filterXSS, filterSQL), response);
    }
 
}

 

/**
 * 安全的HttpServlet(过滤SQL和XSS)
 *
 */
public class HttpServletRequestSafeWrapper extends HttpServletRequestWrapper{
    private boolean filterXSS = true;
 
    private boolean filterSQL = true;
 
    private HttpServletRequest orgRequest = null;
 
    public HttpServletRequestSafeWrapper(HttpServletRequest request) {
        super(request);
        this.orgRequest = request;
    }
 
    public HttpServletRequestSafeWrapper(HttpServletRequest request, boolean filterXSS, boolean filterSQL) {
        super(request);
        orgRequest = request;
        this.filterXSS = filterXSS;
        this.filterSQL = filterSQL;
    }
 
    @Override
    public Enumeration<String> getParameterNames() {
        Set<String> parameterNameSafeList = Sets.newHashSet();
        Enumeration parameterNames = super.getParameterNames();
        while (parameterNames.hasMoreElements()) {
            parameterNameSafeList.add(filterText(String.valueOf(parameterNames.nextElement()), true));
        }
        return Collections.enumeration(parameterNameSafeList);
    }
 
    @Override
    public Map<String, String[]> getParameterMap() {
        Map<String, String[]> parameterSafeMap = Maps.newHashMap();
        Set parameterNameSet = super.getParameterMap().keySet();
        for (Object key : parameterNameSet) {
            parameterSafeMap.put(String.valueOf(key), getParameterValues(String.valueOf(key)));
        }
        return parameterSafeMap;
    }
 
    @Override
    public String[] getParameterValues(String name) {
        String[] values = super.getParameterValues(filterText(name, true));
        if (values == null || values.length == 0) {
            return null;
        }
        int count = values.length;
        String[] safeValues = new String[count];
        for (int i = 0; i < count; i++) {
            // 判断是否为JSON格式数据
            if (isJsonFormat(values[i])) {
                safeValues[i] = filterText(values[i], false);
            } else {
                safeValues[i] = filterText(values[i], true);
            }
 
        }
        return safeValues;
    }
 
    /**
     * 覆盖getParameter方法,将参数名和参数值都做xss & sql过滤。<br/>
     * 如果需要获得原始的值,则通过super.getParameterValues(name)来获取<br/>
     * getParameterNames,getParameterValues和getParameterMap也可能需要覆盖
     */
    @Override
    public String getParameter(String name) {
        String value = super.getParameter(filterText(name, true));
        String safeValue = null;
        if (StringUtils.isNotEmpty(value)) {
            // 判断是否为JSON格式数据
            if (isJsonFormat(value)) {
                safeValue = filterText(value, false);
            } else {
                safeValue = filterText(value, true);
            }
        }
        return safeValue;
    }
 
    /**
     * 覆盖getHeader方法,将参数名和参数值都做xss & sql过滤。<br/>
     * 如果需要获得原始的值,则通过super.getHeaders(name)来获取<br/>
     * getHeaderNames 也可能需要覆盖
     */
    @Override
    public String getHeader(String name) {
        String value = super.getHeader(filterText(name, true));
        String safeValue = null;
        if (StringUtils.isNotEmpty(value)) {
            // 判断是否为JSON格式数据
            if (isJsonFormat(value)) {
                safeValue = filterText(value, false);
            } else {
                safeValue = filterText(value, true);
            }
        }
        return safeValue;
    }
 
    @Override
    public Enumeration<String> getHeaders(String name) {
        Set<String> headerValSafeList = Sets.newHashSet();
        Enumeration headerVals = super.getHeaders(name);
        while (headerVals.hasMoreElements()) {
            headerValSafeList.add(filterText(String.valueOf(headerVals.nextElement()), true));
        }
        return Collections.enumeration(headerValSafeList);
    }
 
    @Override
    public Enumeration<String> getHeaderNames() {
        Set<String> headerNamesSafeList = Sets.newHashSet();
        Enumeration headerNames = super.getHeaderNames();
        while (headerNames.hasMoreElements()) {
            headerNamesSafeList.add(filterText(String.valueOf(headerNames.nextElement()), true));
        }
        return Collections.enumeration(headerNamesSafeList);
    }
 
    /**
     * 判断是否为JSON格式
     *
     * @param json
     * @return
     */
    private boolean isJsonFormat(String json) {
        return StringUtils.isNotEmpty(json) && json.startsWith("{") && json.endsWith("}");
    }
 
    /**
     * 过滤XSS和SQL
     *
     * @param text
     * @param isHtmlEscape
     * @return
     */
    private String filterText(final String text, final boolean isHtmlEscape) {
        String filterText = StringUtils.isEmpty(text)?null:text.trim();
        if (filterXSS) {
            filterText = cleanXSS(filterText);
        }
 
        if (filterSQL) {
            filterText = stripSqlInjection(filterText);
        }
        if (isHtmlEscape) {
            filterText = HtmlUtils.htmlEscape(filterText);
        }
        return filterText;
    }
 
 
    /**
     * 获取最原始的request
     *
     * @return
     */
    public HttpServletRequest getOrgRequest() {
        return orgRequest;
    }
 
    /**
     * 获取最原始的request的静态方法
     *
     * @return
     */
    public static HttpServletRequest getOrgRequest(HttpServletRequest req) {
        if (req instanceof HttpServletRequestSafeWrapper) {
            return ((HttpServletRequestSafeWrapper) req).getOrgRequest();
        }
        return req;
    }
 
    
    private static final List<Pattern> XSS_PATTERN_LIST = Lists.newArrayList(Pattern.compile("<(no)?script[^>]*>.*?</(no)?script>", Pattern.CASE_INSENSITIVE),
            Pattern.compile("eval\\((.*?)\\)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL),
            Pattern.compile("expression\\((.*?)\\)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL),
            Pattern.compile("(javascript:|vbscript:|view-source:)*", Pattern.CASE_INSENSITIVE),
            Pattern.compile("<(\"[^\"]*\"|\'[^\']*\'|[^\'\">])*>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL),
            Pattern.compile("(window\\.location|window\\.|\\.location|document\\.cookie|document\\.|alert\\(.*?\\)|window\\.open\\()*", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL),
            Pattern.compile("<+\\s*\\w*\\s*(oncontrolselect|oncopy|oncut|ondataavailable|ondatasetchanged|ondatasetcomplete|ondblclick|ondeactivate|ondrag|ondragend|ondragenter|ondragleave|ondragover|ondragstart|ondrop|onerror=|onerroupdate|onfilterchange|onfinish|onfocus|onfocusin|onfocusout|onhelp|onkeydown|onkeypress|onkeyup|onlayoutcomplete|onload|onlosecapture|onmousedown|onmouseenter|onmouseleave|onmousemove|onmousout|onmouseover|onmouseup|onmousewheel|onmove|onmoveend|onmovestart|onabort|onactivate|onafterprint|onafterupdate|onbefore|onbeforeactivate|onbeforecopy|onbeforecut|onbeforedeactivate|onbeforeeditocus|onbeforepaste|onbeforeprint|onbeforeunload|onbeforeupdate|onblur|onbounce|oncellchange|onchange|onclick|oncontextmenu|onpaste|onpropertychange|onreadystatechange|onreset|onresize|onresizend|onresizestart|onrowenter|onrowexit|onrowsdelete|onrowsinserted|onscroll|onselect|onselectionchange|onselectstart|onstart|onstop|onsubmit|onunload)+\\s*=+", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL));
 
 
    /**
     * @param value 待处理内容
     * @return
     * @Description 过滤XSS脚本内容
     */
    private static String cleanXSS(String value) {
        if (StringUtils.isNotBlank(value)) {
            Matcher matcher;
            for (Pattern pattern : XSS_PATTERN_LIST) {
                matcher = pattern.matcher(value);
                if (matcher.find()) {
                    value = matcher.replaceAll("");//将带有尖括号的脚本设置为空。在我的框架里如果是转码,还是会报没有相应的属性(尤其是基于hibernate)的情况,所以直接设置成空
                }
            }
            value = value.replaceAll("<", "<").replaceAll(">", ">");
        }
        return value;
    }
 
    /**
     * @param value 待处理内容
     * @return
     * @Description 过滤SQL注入内容
     */
    private static String stripSqlInjection(String value) {
        return StringUtils.isEmpty(value)? null : value.replaceAll("('.+--)|(--)|(%7C)", "");
    }
 
}

代码已全部奉上,快试一试吧

 

补充:

String filterText = StringUtils.isEmpty(text)?null:text.trim(); 这行代码改成下面代码,这个bug巨坑,使用下面的写法是保持原有的参数不变

 String filterText = StringUtils.isEmpty(text)?text:text.trim();//第一个条件 text是NULL还是空,保持原样

展开阅读全文
加载中

作者的其它热门文章

打赏
0
0 收藏
分享
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部