cookie跨站脚本漏洞解决方案
近日项目碰到一个跨脚本注入的问题:
这安全测评工具也是厉害了,直接将脚本注入到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; } }