絕對好用的安全Filter(過濾SQL和XSS)
阿新 • • 發佈:2019-09-13
最近被安保測評搞得頭疼,本來網站上有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)", ""); } }
程式碼已全部奉