之前的旧项目是Java的Web应用程序,是底层的Servlet开发的,最近被检测出有XSS漏洞。研究了一下,主要是针对Content-Type、请求参数上做攻击,JavaWeb可以使用Filter过滤器,来对这些统一进行过滤。
实现
首先实现安全过滤器
- 新建 XSSRequestWrapper.java,针对请求参数关键词做检测和过滤
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequestWrapper; import java.util.regex.Pattern; /** * @description */ public class XSSRequestWrapper extends HttpServletRequestWrapper { public XSSRequestWrapper(HttpServletRequest request) { super(request); } private static Pattern[] patterns = new Pattern[]{ // Script fragments Pattern.compile("<(no)?script[^>]*>.*?</(no)?script>", Pattern.CASE_INSENSITIVE), Pattern.compile("<(\"[^\"]*\"|\'[^\']*\'|[^\'\">])*>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL), Pattern.compile("\\(.*\\)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL), Pattern.compile("<.*>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL), // src='...' Pattern.compile("src[\r\n]*=[\r\n]*\\'(.*?)\\'", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL), Pattern.compile("src[\r\n]*=[\r\n]*\\\"(.*?)\\\"", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL), // lonely script tags Pattern.compile("</script>", Pattern.CASE_INSENSITIVE), Pattern.compile("<script(.*?)>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL), Pattern.compile("<iframe(.*?)>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL), // eval(...) Pattern.compile("eval\\((.*?)\\)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL), // expression(...) Pattern.compile("expression\\((.*?)\\)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL), // javascript:... vbscript:... Pattern.compile("(javascript:|vbscript:|view-source:)*", Pattern.CASE_INSENSITIVE), Pattern.compile("<(\"[^\"]*\"|\'[^\']*\'|[^\'\">])*>", 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), Pattern.compile("(window\\.location|window\\.|\\.location|document\\.cookie|document\\.|alert\\(.*?\\)|window\\.open\\()*", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL), // onload(...)=... Pattern.compile("onload(.*?)=", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL) }; @Override public String[] getParameterValues(String parameter) { String[] values = super.getParameterValues(parameter); if (values == null) { return null; } int count = values.length; String[] encodedValues = new String[count]; for (int i = 0; i < count; i++) { encodedValues[i] = stripXSS(values[i]); } return encodedValues; } @Override public String getParameter(String parameter) { String value = super.getParameter(parameter); return stripXSS(value); } @Override public String getHeader(String name) { String value = super.getHeader(name); return stripXSS(value); } private String stripXSS(String value) { if (value != null) { // NOTE: It's highly recommended to use the ESAPI library and uncomment the following line to // avoid encoded attacks. // value = ESAPI.encoder().canonicalize(value); // Avoid null characters value = value.replaceAll("\0", "").replace("(","("); // Remove all sections that match a pattern for (Pattern scriptPattern : patterns){ value = scriptPattern.matcher(value).replaceAll(""); } } return value; } }
- 新建 SecurityFilter.java
import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.net.URLDecoder; /** * @description */ public class SecurityFilter implements Filter { public static final String[] ALLOWED_CONTENT_TYPES = new String[]{ "application/x-www-form-urlencoded", "multipart/form-data", "text/plain" }; @Override public void destroy() { } @Override public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { if (req instanceof HttpServletRequest) { doFilter((HttpServletRequest) req, (HttpServletResponse) res, chain); } else { res.setContentType("text/html;charset=UTF-8"); res.getWriter().write("Illegal Request"); //chain.doFilter(req, res); return; } } @SuppressWarnings("rawtypes") public void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException { String contentType = request.getHeader("content-type"); if (contentType != null && !isAllowedContentType(contentType)) { response.setContentType("text/html;charset=UTF-8"); response.getWriter().write("Illegal Content-Type"); System.err.println("Illegal Content-Type: " + (contentType.length() > 120 ? (contentType.substring(0, 120) + "...") : contentType)); return; } String queryString = request.getQueryString(); if (queryString != null) { queryString = URLDecoder.decode(queryString, "UTF-8"); if (queryString.contains(">") && queryString.contains(")") && (queryString.contains("'") || queryString.contains("\""))) { response.setContentType("text/html;charset=UTF-8"); response.getWriter().write("Illegal Query String"); return; } } chain.doFilter(new XSSRequestWrapper(request), response); } private boolean isAllowedContentType(String contentType) { for (String allowedContentType : ALLOWED_CONTENT_TYPES) { if (contentType.startsWith(allowedContentType)) return true; } return false; } @Override public void init(FilterConfig config) { } }
下面配置过滤器,在系统中生效
- 修改 web.xml,增加对SecurityFilter的配置
<filter> <filter-name>SecurityFilter</filter-name> <filter-class> com.terrynow.filter.SecurityFilter </filter-class> </filter> <filter-mapping> <filter-name>SecurityFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
文章评论