SpringMVC 增加了一个 xss 过滤器,导致 Controller 上传的文件为空
資深大佬 : liugp5201314 10
最近做的一个项目进行安全测试时测出了 SQL 注入问题,严重级别为高危,怎么办呢?我还是个雏,还没学会飞呢,挠挠头,硬上吧,然后把之前项目里的 xss 都弄过来修修改改,然后跑起来,震惊了,竟然全都过滤了,是的,全都过滤了,连上传的文件都给我过滤了,咋办?再百度,结果全是千篇一律的抄袭,没一个能用的,还是发个帖子大家帮我瞅瞅,看看怎么解决一下,头发都挠掉一大把了,听说植发一根二十块,听着都吓人。
这是调用过滤器:
```java public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException{ HttpServletRequest httpRequest = (HttpServletRequest) request; HttpServletResponse httpResponse = (HttpServletResponse) response; String enctype = request.getContentType(); if (StringUtils.isNotBlank(enctype) && enctype.contains("multipart/form-data")) { final MultipartResolver multipartResolver = SpringUtil.getBean("multipartResolver"); final MultipartHttpServletRequest multipartHttpServletRequest = multipartResolver.resolveMultipart((HttpServletRequest) request); chain.doFilter(new XssHttpServletRequestWrapper(multipartHttpServletRequest), response); } else { chain.doFilter(new XssHttpServletRequestWrapper((HttpServletRequest) request), response); } }
这是重写的方法: ```java public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper { /** * @param request */ public XssHttpServletRequestWrapper(HttpServletRequest request) { super(request); } /** * 覆盖 getHeader 方法,将参数名和参数值都做 xss 过滤。 * 如果需要获得原始的值,则通过 super.getHeaders(name)来获取 * getHeaderNames 也可能需要覆盖 */ @Override public String getHeader(String name) { String value = super.getHeader(EscapeUtil.escape(name)); if (value != null) { value = EscapeUtil.escape(value); } return value; } @Override public String getParameter(String name){ String value = super.getParameter(name); if (value != null) { String escapseValue = EscapeUtil.escape(value.trim()); return escapseValue; } return super.getParameter(name); } @Override public String[] getParameterValues(String name) { String[] values = super.getParameterValues(name); if (values != null) { int length = values.length; String[] escapseValues = new String[length]; for (int i = 0; i < length; i++) { // 防 xss 攻击和过滤前后空格 escapseValues[i] = EscapeUtil.escape(values[i]).trim(); } return escapseValues; } return super.getParameterValues(name); } }
这是过滤规则:
public class EscapeUtil { public static final String RE_HTML_MARK = "(<[^<]*?>)|(<[\s]*?/[^<]*?>)|(<[^<]*?/[\s]*?>)"; private static final char[][] TEXT = new char[64][]; static { for (int i = 0; i < 64; i++) { TEXT[i] = new char[] { (char) i }; } // special HTML characters TEXT['''] = "'".toCharArray(); // 单引号 TEXT['"'] = """.toCharArray(); // 单引号 TEXT['&'] = "&".toCharArray(); // &符 TEXT['<'] = "<".toCharArray(); // 小于号 TEXT['>'] = ">".toCharArray(); // 大于号 } /** * 转义文本中的 HTML 字符为安全的字符 * * @param text 被转义的文本 * @return 转义后的文本 */ public static String escape(String text) { return encode(text); } /** * 还原被转义的 HTML 特殊字符 * * @param content 包含转义符的 HTML 内容 * @return 转换后的字符串 */ public static String unescape(String content) { return decode(content); } /** * 清除所有 HTML 标签,但是不删除标签内的内容 * * @param content 文本 * @return 清除标签后的文本 */ public static String clean(String content) { return new HTMLFilter().filter(content); } /** * Escape 编码 * * @param text 被编码的文本 * @return 编码后的字符 */ private static String encode(String text) { int len; if ((text == null) || ((len = text.length()) == 0)) { return StringUtils.EMPTY; } StringBuilder buffer = new StringBuilder(len + (len >> 2)); char c; for (int i = 0; i < len; i++) { c = text.charAt(i); if (c < 64) { buffer.append(TEXT[c]); } else { buffer.append(c); } } return buffer.toString(); } /** * Escape 解码 * * @param content 被转义的内容 * @return 解码后的字符串 */ public static String decode(String content) { if (StringUtils.isEmpty(content)) { return content; } StringBuilder tmp = new StringBuilder(content.length()); int lastPos = 0, pos = 0; char ch; while (lastPos < content.length()) { pos = content.indexOf("%", lastPos); if (pos == lastPos) { if (content.charAt(pos + 1) == 'u') { ch = (char) Integer.parseInt(content.substring(pos + 2, pos + 6), 16); tmp.append(ch); lastPos = pos + 6; } else { ch = (char) Integer.parseInt(content.substring(pos + 1, pos + 3), 16); tmp.append(ch); lastPos = pos + 3; } } else { if (pos == -1) { tmp.append(content.substring(lastPos)); lastPos = content.length(); } else { tmp.append(content.substring(lastPos, pos)); lastPos = pos; } } } return tmp.toString(); } public static void main(String[] args) { String html = "<script>alert(1);</script>"; // String html = "<scr<script>ipt>alert("XSS")</scr<script>ipt>"; // String html = "<123"; System.out.println(EscapeUtil.clean(html)); System.out.println(EscapeUtil.escape(html)); System.out.println(EscapeUtil.unescape(html)); } }
大佬有話說 (11)