近日項目碰到一個跨腳本注入的問題:
這安全測評工具也是厲害了,直接將腳本注入到cookie里頭,以前沒有碰到這樣的情況。
之前寫過一篇文章過濾跨腳本注入的問題。《淺談XSS攻擊原理與解決方法》關於跨腳本注入的問題,不曉得原理的同學可以看下。但是里頭沒有處理cookie注入的問題。接下來介紹下如何處理。
關鍵代碼在這里:首先獲取到cookie,檢查下是否有敏感字符,如果有的話,就進行替換。
//防止cookie跨站注入問題,替換敏感字符。 Cookie[] cookies = ((HttpServletRequest) request).getCookies(); if(cookies!=null) { for(Cookie cookie: cookies){ if(CookieFilterUtil.isValidate(cookie.getValue())){ cookie.setValue(CookieFilterUtil.xssClean(cookie.getValue())); Cookie newCookie=cookie; cookie.setMaxAge(0); ((HttpServletResponse) response).addCookie(newCookie); break; } } }
CookieFilter.java
public class CookieFilterUtil { //將敏感字符進行替換 public static String xssClean(String value) { //ClassLoaderUtils.getResourceAsStream("classpath:antisamy-slashdot.xml", XssHttpServletRequestWrapper.class) if (value != null) { // NOTE: It's highly recommended to use the ESAPI library and // uncomment the following line to // avoid encoded attacks. // value = encoder.canonicalize(value); value = value.replaceAll("\0", ""); // Avoid anything between script tags Pattern scriptPattern = Pattern.compile("<script>(.*?)</script>", Pattern.CASE_INSENSITIVE); value = scriptPattern.matcher(value).replaceAll(""); // Avoid anything in a src='...' type of expression scriptPattern = Pattern.compile("src[\r\n]*=[\r\n]*\\\'(.*?)\\\'", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL); value = scriptPattern.matcher(value).replaceAll(""); // Avoid anything in a href='...' type of expression scriptPattern = Pattern.compile("href[\r\n]*=[\r\n]*\\\"(.*?)\\\"", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL); value = scriptPattern.matcher(value).replaceAll(""); // Remove any lonesome </script> tag scriptPattern = Pattern.compile("</script>", Pattern.CASE_INSENSITIVE); value = scriptPattern.matcher(value).replaceAll(""); // Remove any lonesome <script ...> tag scriptPattern = Pattern.compile("<script(.*?)>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL); value = scriptPattern.matcher(value).replaceAll(""); // Avoid eval(...) expressions scriptPattern = Pattern.compile("eval\\((.*?)\\)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL); value = scriptPattern.matcher(value).replaceAll(""); // Avoid expression(...) expressions scriptPattern = Pattern.compile("expression\\((.*?)\\)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL); value = scriptPattern.matcher(value).replaceAll(""); // Avoid javascript:... expressions scriptPattern = Pattern.compile("javascript:", Pattern.CASE_INSENSITIVE); value = scriptPattern.matcher(value).replaceAll(""); // Avoid vbscript:... expressions scriptPattern = Pattern.compile("vbscript:", Pattern.CASE_INSENSITIVE); value = scriptPattern.matcher(value).replaceAll(""); // Avoid onload= expressions scriptPattern = Pattern.compile("onload(.*?)=", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL); value = scriptPattern.matcher(value).replaceAll(""); } return value; } //校驗是否含有敏感字符 public static boolean isValidate(String value) { //ClassLoaderUtils.getResourceAsStream("classpath:antisamy-slashdot.xml", XssHttpServletRequestWrapper.class) boolean flag=false; if (value != null) { // NOTE: It's highly recommended to use the ESAPI library and // uncomment the following line to // avoid encoded attacks. // value = encoder.canonicalize(value); value = value.replaceAll("\0", ""); // Avoid anything between script tags Pattern scriptPattern = Pattern.compile("<script>(.*?)</script>", Pattern.CASE_INSENSITIVE); if(scriptPattern.matcher(value).find()){ flag=true; return flag; } // Avoid anything in a src='...' type of expression scriptPattern = Pattern.compile("src[\r\n]*=[\r\n]*\\\'(.*?)\\\'", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL); if(scriptPattern.matcher(value).find()){ flag=true; return flag; } // Avoid anything in a href='...' type of expression scriptPattern = Pattern.compile("href[\r\n]*=[\r\n]*\\\"(.*?)\\\"", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL); if(scriptPattern.matcher(value).find()){ flag=true; return flag; } // Remove any lonesome </script> tag scriptPattern = Pattern.compile("</script>", Pattern.CASE_INSENSITIVE); if(scriptPattern.matcher(value).find()){ flag=true; return flag; } // Remove any lonesome <script ...> tag scriptPattern = Pattern.compile("<script(.*?)>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL); if(scriptPattern.matcher(value).find()){ flag=true; return flag; } // Avoid eval(...) expressions scriptPattern = Pattern.compile("eval\\((.*?)\\)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL); if(scriptPattern.matcher(value).find()){ flag=true; return flag; } // Avoid expression(...) expressions scriptPattern = Pattern.compile("expression\\((.*?)\\)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL); if(scriptPattern.matcher(value).find()){ flag=true; return flag; } // Avoid javascript:... expressions scriptPattern = Pattern.compile("javascript:", Pattern.CASE_INSENSITIVE); if(scriptPattern.matcher(value).find()){ flag=true; return flag; } // Avoid vbscript:... expressions scriptPattern = Pattern.compile("vbscript:", Pattern.CASE_INSENSITIVE); if(scriptPattern.matcher(value).find()){ flag=true; return flag; } // Avoid onload= expressions scriptPattern = Pattern.compile("onload(.*?)=", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL); if(scriptPattern.matcher(value).find()){ flag=true; return flag; } } return flag; } }
XssFilter.java
public class XssFilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { //防止cookie跨站注入問題,替換敏感字符。 boolean flag=false; Cookie[] cookies = ((HttpServletRequest) request).getCookies(); if(cookies!=null) { for(Cookie cookie: cookies){ if(CookieFilterUtil.isValidate(cookie.getValue())){ flag=true; cookie.setValue(CookieFilterUtil.xssClean(cookie.getValue())); Cookie newCookie=cookie; cookie.setMaxAge(0); ((HttpServletResponse) response).addCookie(newCookie); break; } } } //如果存在跨站注入問題,跳轉到登錄頁。 if(!flag){ String path = ((HttpServletRequest) request).getContextPath(); String protAndPath = request.getServerPort() == 80 ? "" : ":" + request.getServerPort(); String basePath = request.getScheme() + "://" + request.getServerName() + protAndPath + path + "/"; String returnUrl = basePath+"login.do"; request.setCharacterEncoding("UTF-8"); response.setContentType("text/html; charset=UTF-8"); // 轉碼 response.getWriter().println( "<script language=\"javascript\">if(window.opener==null){window.top.location.href=\"" + returnUrl + "\";}else{window.opener.top.location.href=\"" + returnUrl + "\";window.close();}</script>"); }else{ chain.doFilter(new XssHttpServletRequestWrapper((HttpServletRequest) request), response); } } @Override public void destroy() { } }
這兒的操作如果有敏感字符,那么就調整到登錄首頁,刪除cookie,當然也可以不處理,像以下代碼這樣
//防止cookie跨站注入問題,替換敏感字符。 Cookie[] cookies = ((HttpServletRequest) request).getCookies(); if(cookies!=null) { for(Cookie cookie: cookies){ if(CookieFilterUtil.isValidate(cookie.getValue())){ cookie.setValue(CookieFilterUtil.xssClean(cookie.getValue())); Cookie newCookie=cookie; cookie.setMaxAge(0); ((HttpServletResponse) response).addCookie(newCookie); break; } } } chain.doFilter(new XssHttpServletRequestWrapper((HttpServletRequest) request), response);
xss過濾器實現XssHttpServletRequestWrapper.java
public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper { public XssHttpServletRequestWrapper(HttpServletRequest request) { super(request); } @SuppressWarnings("rawtypes") public Map<String,String[]> getParameterMap(){ Map<String,String[]> request_map = super.getParameterMap(); Iterator iterator = request_map.entrySet().iterator(); while(iterator.hasNext()){ Map.Entry me = (Map.Entry)iterator.next(); String[] values = (String[])me.getValue(); for(int i = 0 ; i < values.length ; i++){ values[i]=HtmlRegexpUtil.fiterHtmlByTagArr(values[i]); values[i] = xssClean(values[i]); } } return request_map; } public String[] getParameterValues(String paramString) { String[] arrayOfString1 = super.getParameterValues(paramString); if (arrayOfString1 == null) return null; int i = arrayOfString1.length; String[] arrayOfString2 = new String[i]; for (int j = 0; j < i; j++){ arrayOfString1[j]=HtmlRegexpUtil.fiterHtmlByTagArr(arrayOfString1[j]); arrayOfString2[j] = xssClean(arrayOfString1[j]); } return arrayOfString2; } public String getParameter(String paramString) { String str = super.getParameter(paramString); if (str == null) return null; return xssClean(str); } public String getHeader(String paramString) { String str = super.getHeader(paramString); if (str == null) return null; str = str.replaceAll("\r|\n", ""); return xssClean(str); } private String xssClean(String value) { //ClassLoaderUtils.getResourceAsStream("classpath:antisamy-slashdot.xml", XssHttpServletRequestWrapper.class) if (value != null) { // NOTE: It's highly recommended to use the ESAPI library and // uncomment the following line to // avoid encoded attacks. // value = encoder.canonicalize(value); value = value.replaceAll("\0", ""); // Avoid anything between script tags Pattern scriptPattern = Pattern.compile("<script>(.*?)</script>", Pattern.CASE_INSENSITIVE); value = scriptPattern.matcher(value).replaceAll(""); // Avoid anything in a src='...' type of expression scriptPattern = Pattern.compile("src[\r\n]*=[\r\n]*\\\'(.*?)\\\'", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL); value = scriptPattern.matcher(value).replaceAll(""); // Avoid anything in a href='...' type of expression scriptPattern = Pattern.compile("href[\r\n]*=[\r\n]*\\\"(.*?)\\\"", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL); value = scriptPattern.matcher(value).replaceAll(""); // Remove any lonesome </script> tag scriptPattern = Pattern.compile("</script>", Pattern.CASE_INSENSITIVE); value = scriptPattern.matcher(value).replaceAll(""); // Remove any lonesome <script ...> tag scriptPattern = Pattern.compile("<script(.*?)>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL); value = scriptPattern.matcher(value).replaceAll(""); // Avoid eval(...) expressions scriptPattern = Pattern.compile("eval\\((.*?)\\)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL); value = scriptPattern.matcher(value).replaceAll(""); // Avoid expression(...) expressions scriptPattern = Pattern.compile("expression\\((.*?)\\)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL); value = scriptPattern.matcher(value).replaceAll(""); // Avoid javascript:... expressions scriptPattern = Pattern.compile("javascript:", Pattern.CASE_INSENSITIVE); value = scriptPattern.matcher(value).replaceAll(""); // Avoid vbscript:... expressions scriptPattern = Pattern.compile("vbscript:", Pattern.CASE_INSENSITIVE); value = scriptPattern.matcher(value).replaceAll(""); // Avoid onload= expressions scriptPattern = Pattern.compile("onload(.*?)=", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL); value = scriptPattern.matcher(value).replaceAll(""); } return value; } }