过滤器代码如下:
package com.eshore.itmp.model.web.security; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.net.URLDecoder; import java.util.regex.Pattern; import javax.servlet.FilterChain; import javax.servlet.RequestDispatcher; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.web.filter.OncePerRequestFilter; /** * XSS漏洞过滤器 * * @author ahomeeye * */ public class XssFilter extends OncePerRequestFilter { private static Log logger = LogFactory.getLog(XssFilter.class); private String forwardUrl = "/login.jsp"; @Override protected void doFilterInternal(HttpServletRequest req, HttpServletResponse res, FilterChain filterChain) throws ServletException, IOException { String uri = req.getRequestURI(); logger.info("-------uri=" + uri); // 过滤请求的参数名和参数值 XssHttpServletRequestWrapper xssRequest = new XssHttpServletRequestWrapper( (HttpServletRequest) req); if (uri.contains("j_spring_security_check") && isContainSpecialChar(uri)) {// 判断uri是否合法 logger.info("-------uri包含特殊字符 uri=" + xssRequest.getRequestURI()); RequestDispatcher dispatcher = xssRequest .getRequestDispatcher(forwardUrl); dispatcher.forward(xssRequest, res); } else { filterChain.doFilter(xssRequest, res);// 执行其他过滤操作 } } /** * 判断字符串是否包含特殊字符 * * @param strVal * @return */ public boolean isContainSpecialChar(String value) { String strVal = ""; try { strVal = URLDecoder.decode(value, "UTF-8"); } catch (UnsupportedEncodingException e) { logger.error("URL解码错误", e); } boolean flag = false; if (match(".*?<script\\b.*?>*?</script>.*?", strVal)) {// //匹配js脚本字符串 flag = true; } else if (match(".*?<.*?>.*?", strVal)) {// 匹配html标签字符串 flag = true; } return flag; } /** * 匹配正则表达式,大小写不敏感 * * @param regex * @param input * @return */ public boolean match(String regex, CharSequence input) { Pattern p = Pattern.compile(regex, Pattern.CASE_INSENSITIVE); return p.matcher(input).matches(); } public String getForwardUrl() { return forwardUrl; } public void setForwardUrl(String forwardUrl) { this.forwardUrl = forwardUrl; } }
在xml文件中声明过滤器,配置过滤器链
<beans:bean id="xssFilter" class="com.eshore.itmp.model.web.security.XssFilter" p:forwardUrl="/login.jsp"> </beans:bean> <beans:bean id="springSecurityFilterChain" class="org.springframework.security.web.FilterChainProxy"> <filter-chain-map path-type="ant"> <filter-chain pattern="/kaptcha.validate" filters="none" /> <filter-chain pattern="/services/**" filters="none" /> <filter-chain pattern="/media/**" filters="none" /> <filter-chain pattern="/css/**" filters="none" /> <filter-chain pattern="/images/**" filters="none" /> <filter-chain pattern="/js/**" filters="none" /> <filter-chain pattern="/page/**" filters="none" /> <filter-chain pattern="/My97DatePicker/**" filters="none" /> <filter-chain pattern="/login.jsp" filters="none" /> <filter-chain pattern="/phytopo/**" filters="none" /> <filter-chain pattern="/relationView/**" filters="none" /> <!-- add --> <filter-chain pattern="/oaLogin.do" filters="none" /> <filter-chain pattern="/selection/**" filters="none" /> <filter-chain pattern="/entity/**" filters="none" /> <filter-chain pattern="/upload/**" filters="none" /> <filter-chain pattern="/assessmentKpi/getExcelData.do**" filters="none" /> <!-- add --> <filter-chain pattern="/logoutSuccess.jsp" filters="none" /> <filter-chain pattern="/**" filters=" xssFilter, securityContextPersistenceFilter, logoutFilter, usernamePasswordAuthenticationFilter, anonymousAuthenticationFilter, exceptionTranslationFilter, itmpCustomInterceptor, sysLogFilter" /> </filter-chain-map> </beans:bean>
重写HttpServletRequest,覆盖一些方法
package com.eshore.itmp.model.web.security; import java.io.UnsupportedEncodingException; import java.net.URLDecoder; import java.util.regex.Pattern; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequestWrapper; /** * 请求参数过滤器(修复跨站脚本漏洞) * * @author ahomeeye * */ public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper { HttpServletRequest orgRequest = null; public XssHttpServletRequestWrapper(HttpServletRequest request) { super(request); orgRequest = request; } /** * 覆盖getParameter方法,将参数名和参数值都做xss过滤。<br/> * 如果需要获得原始的值,则通过super.getParameterValues(name)来获取<br/> * getParameterNames,getParameterValues和getParameterMap也可能需要覆盖 */ @Override public String getParameter(String name) { String value = super.getParameter(xssEncode(name)); if (value != null) { value = xssEncode(value); } return value; } @Override public Object getAttribute(String name) { Object value = super.getAttribute(xssEncode(name)); if (value instanceof String) { return xssEncode(value.toString()); } return value; } /** * 覆盖getHeader方法,将参数名和参数值都做xss过滤。<br/> * 如果需要获得原始的值,则通过super.getHeaders(name)来获取<br/> * getHeaderNames 也可能需要覆盖 */ @Override public String getHeader(String name) { String value = super.getHeader(xssEncode(name)); if (value != null) { value = xssEncode(value); } return value; } @Override public String getRequestURI() { String strVal = ""; try { strVal = URLDecoder.decode(super.getRequestURI(), "UTF-8"); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } String value = xssEncode(strVal); if (value != null) { value = xssEncode(value); } return value; } public String escape(String s) { StringBuilder sb = new StringBuilder(s.length() + 16); for (int i = 0; i < s.length(); i++) { char c = s.charAt(i); switch (c) { case '>': sb.append('>');// 全角大于号 break; case '<': sb.append('<');// 全角小于号 break; case '\'': sb.append('‘');// 全角单引号 break; case '\"': sb.append('“');// 全角双引号 break; case '\\': sb.append('\');// 全角斜线 break; case '%': sb.append('%'); // 全角冒号 break; case ';': sb.append(';'); // 全角分号 break; default: sb.append(c); break; } } return sb.toString(); } /** * 将容易引起xss漏洞的半角字符直接替换成全角字符 * * @param s * @return */ public String xssEncode(String s) { if (s == null || s.isEmpty()) { return s; } String result = stripXSS(s); if (null != result) { result = escape(result); } return result; } 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("", ""); // 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(""); scriptPattern = Pattern.compile("src[\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(""); scriptPattern = Pattern.compile("<iframe>(.*?)</iframe>", Pattern.CASE_INSENSITIVE); value = scriptPattern.matcher(value).replaceAll(""); scriptPattern = Pattern.compile("</iframe>", Pattern.CASE_INSENSITIVE); value = scriptPattern.matcher(value).replaceAll(""); // Remove any lonesome <script ...> tag scriptPattern = Pattern.compile("<iframe(.*?)>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL); value = scriptPattern.matcher(value).replaceAll(""); value = value.replace(";", ""); value = value.replace("<", ""); value = value.replace(">", ""); } return value; } /** * 获取最原始的request * * @return */ public HttpServletRequest getOrgRequest() { return orgRequest; } @Override public HttpServletRequest getRequest() { return orgRequest; } /** * 获取最原始的request的静态方法 * * @return */ public static HttpServletRequest getOrgRequest(HttpServletRequest req) { if (req instanceof XssHttpServletRequestWrapper) { return ((XssHttpServletRequestWrapper) req).getOrgRequest(); } return req; } }
最后,给个页面表单例子:
<script type="text/javascript"> function setPass(originId,decId){ var orginName = window.parent.document.getElementById('orginName').value; var newName = strEnc(orginName,'itmp','des','encrypt'); window.parent.document.getElementById('userId').value = newName; var orginPass = window.parent.document.getElementById(originId).value; var newPass = strEnc(orginPass,'itmp','des','encrypt'); //alert('newPass=' + newPass + ' pass='+strDec(newPass,'itmp','des','encrypt')); window.parent.document.getElementById(decId).value = newPass; } </script> <form id="form" name="form" action="${ctx}/j_spring_security_check" method="post"> <table width="100%" border="0" cellspacing="0" cellpadding="0"> <tr> <td width="20%" height="10" class="black14" align="center" valign="middle"></td> <td colspan="2" align="left"> <div class="error${param.error==true?'':'hide'}" style="color:red;"> ${sessionScope['SPRING_SECURITY_LAST_EXCEPTION'].message} </div> </td> </tr> <tr> <td width="20%" height="35" class="black14">用户名</td> <td colspan="2" align="left"><label > <input type="text" id="orginName" value="${sessionScope['SPRING_SECURITY_LAST_USERNAME']}" class="input" /> <input type="hidden" id="userId" name="j_username" value="" class="input"/> </label></td> </tr> <tr> <td height="35" class="black14">密 码</td> <td colspan="2" align="left"><label > <input type="password" id="orginPass" class="input" onblur="setPass('orginPass','password');"/> <input type="hidden" name="j_password" id="password" class="input"/> </label></td> </tr> <tr> <td height="35" class="black14">验证码</td> <td width="37%" align="left"><label> <input type="text" name="validateCode" class="input2"/> </label></td> <td width="43%" align="left"> <img onclick="this.src='kaptcha.validate'" src="kaptcha.validate" width="77" height="28" /> </td> </tr> <tr> <td height="93" colspan="3" align="center"> <a href="#"> <input type="image" src="${ctx}/css/remake/images/button_login.gif" width="111" height="36" border="0" /> </a> </td> </tr> </table> </form>
参考网址:
http://yunjiechao-163-com.iteye.com/blog/1973803
http://www.cnblogs.com/wuhuacong/archive/2013/04/15/3022011.html