1. 程式人生 > >絕對好用的安全Filter(過濾SQL和XSS)

絕對好用的安全Filter(過濾SQL和XSS)

最近被安保測評搞得頭疼,本來網站上有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)", "");
    }
 
}

程式碼已全部奉