SpringMVC 增加了一个 xss 过滤器,导致 Controller 上传的文件为空 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
liugp5201314
V2EX    Java

SpringMVC 增加了一个 xss 过滤器,导致 Controller 上传的文件为空

  •  2
     
  •   liugp5201314 2020-05-07 14:22:09 +08:00 3703 次点击
    这是一个创建于 2062 天前的主题,其中的信息可能已经有所发展或是发生改变。

    最近做的一个项目进行安全测试时测出了 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['\''] = "&#039;".toCharArray(); // 单引号 TEXT['"'] = "&#34;".toCharArray(); // 单引号 TEXT['&'] = "&#38;".toCharArray(); // &符 TEXT['<'] = "&#60;".toCharArray(); // 小于号 TEXT['>'] = "&#62;".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)); } } 
    16 条回复    2020-05-16 14:58:29 +08:00
    waa
        1
    waa  
       2020-05-07 17:02:00 +08:00
    在 controller 方法参数中直接添加 MultipartHttpServletRequest 形式参数,通过 multipartHttpservletRequest 对象获取 MultipartFile 和其余的请求参数。我的是这样解决的
    jzmws
        2
    jzmws  
       2020-05-07 20:11:42 +08:00
    forty 测试的 ??
    dushixiang
        3
    dushixiang  
       2020-05-07 20:48:59 +08:00
    1. sql 注入你加 xss 过滤器有啥作用?
    2. 判断 ContentType 之后的处理有问题。
    sagaxu
        4
    sagaxu  
       2020-05-07 21:00:14 +08:00 via Android
    你这个思路很 php
    richard1122
        5
    richard1122  
       2020-05-07 21:14:00 +08:00
    想起了无数年前 PHP 开的 magic quote
    chendy
        6
    chendy  
       2020-05-08 07:59:33 +08:00
    建议直接写 servlet 算了,这 springmvc 用的不如不用
    liugp5201314
        7
    liugp5201314  
    OP
       2020-05-08 10:33:44 +08:00
    @FreeEx 第一次弄,我也是百度的,说 xss 是防 sql 注入的
    liugp5201314
        8
    liugp5201314  
    OP
       2020-05-08 10:35:11 +08:00
    @waa 是吧原来 controller 里的 HttpServletRequest 这个参数替换为 MultipartHttpServletRequest 这个吗
    MrMario
        9
    MrMario  
       2020-05-08 14:42:09 +08:00
    注入问题根源是 sql 的处理,预编译+参数绑定,或者使用 ORM 框架就好(个别框架使用需注意)
    liugp5201314
        10
    liugp5201314  
    OP
       2020-05-09 10:30:21 +08:00
    @MrMario 框架已经订好了,这是个维护的项目,我只能改功能,不能改框架
    ice2neet
        11
    ice2neet  
       2020-05-09 13:43:24 +08:00
    sql 注入不是应该处理 sql 吗?
    BryceL
        12
    BryceL  
       2020-05-12 11:17:53 +08:00
    写个 AOP 对入参进行处理。你这 sql 注入是指的参数的问题把。
    xinQing
        13
    xinQing  
       2020-05-13 18:01:53 +08:00
    哈哈,我遇到过类似的问题。搞了个过滤器过滤请求内容,然后 controller 里面的数据拿不到了。这是因为正常情况下流只能处理一次,你过滤器消费了,后续就没有了。你要采用 warpper 包装 Request,让 Request 支持可重复消费。spring 可以用这个包装下 org.springframework.web.util.ContentCachingRequestWrapper
    liugp5201314
        14
    liugp5201314  
    OP
       2020-05-14 10:14:08 +08:00
    @xinQing 你看我上边的代码。我已经重写了 warpper 了。但还是不行,不知是不是哪里写的不对
    xinQing
        15
    xinQing  
       2020-05-15 09:27:54 +08:00
    @liugp5201314 说了啊,你写的有问题,你看看 org.springframework.web.util.ContentCachingRequestWrapper 用 ByteArrayInputStream 缓存数据,使流支持可重复读取
    340244120w
        16
    340244120w  
       2020-05-16 14:58:29 +08:00
    上传的接口 直接 chain.doFilter(request, response)就行了。。不用包装
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     986 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 26ms UTC 18:43 PVG 02:43 LAX 10:43 JFK 13:43
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86