首页 > 技术文章 > 工作问题随笔记录-追加Filter过滤器踩坑记录

aoshicangqiong 2022-05-29 23:15 原文

一、使用Filter过滤器无法拦截XSS脚本攻击

1、背景描述:近期接手一个老项目,技术栈十分陈旧,使用Struts2+hibernate+JSP技术栈;老项目被阉割后要给中国移动上线使用,用户质控团队测试发现,系统健壮性存在XSS脚本攻击&表情包等问题;本能考虑使用Filter过滤器,处理XSS脚本攻击和表情包存储报错问题(质控发现的其他问题,本次不做详细说明);

2、问题描述:配置XSS脚本攻击过滤器(网上随处可查到),配置过程十分简单此处不在详述;按照步骤配置完后,验证发现XSS脚本注入时,带有恶意脚本的请求可被过滤到,有些带有恶意脚本的请求无法被过滤掉;开始怀疑人生,瞬间整个人生都不美丽了;此处补充一下XSS过滤器处理机制:使用Filter过滤器,拦截检查用户的访问请求中是否带有恶意攻击脚本,若发现存在恶意脚本,将对请求中的恶意脚本做相关处理,通常是将恶意脚本过滤掉或替换为空字符串;若请求中未发现恶意脚本,过滤器不做任何处理直接放行;

3、问题原因:经排查发现包含附件上传的form表单提交,Filter过滤器完全不起作用;普通的form表单提交,Filter过滤器可以正常起作用;最终定位原因是form表单的enctype属性是导致Filter过滤器失效的根本原因;enctype编码类型为multipart/form-data的类型,Filter过滤器对提交参数不做拦截;

 

4、form表单的enctype属性:

1)定义和用法:
  a>enctype属性规定在发送到服务器之前应该如何对表单数据进行编码。
  b>默认地,表单数据会编码为 "application/x-www-form-urlencoded"。就是说,在发送到服务器之前,所有字符都会进行编码(空格转换为 "+" 加号,特殊符号转换为 ASCII HEX 值)。
2)属性名值和对应描述:
application/x-www-form-urlencoded在发送前默认编码所有字符;multipart/form-data:不对字符编码,若使用包含文件上传控件的表单时,必须使用该类型;text/plain:空格转换为 "+" 加号,但不对特殊字符编码。

3)详细说明:
form的enctype属性为编码方式,常用有两种:application/x-www-form-urlencoded和multipart/form-data,
a>当action为get时候,浏览器用x-www-form-urlencoded的编码方式把form数据转换成一个字串(name1=value1&name2=value2...),然后把这个字串增加到url后面,用?分割,加载这个新的url。
b>当action为post时候,浏览器把form表单数据封装到HTTP头信息中,然后发送到server。
c>如果没有type=file的控件,用默认的application/x-www-form-urlencoded就可以了。
d>但是如果有type=file的话,就要用到multipart/form-data了。浏览器会把整个表单以控件为单位分割,并为每个部分加上Content-Disposition(form-data或者file),Content-Type(默认为text/plain),name(控件name)等信息,并加上分割符(boundary)。

5、本人解决办法:

1)查阅材料发现SpringMVC解决办法比较简单只管,方案如下所示:

 

  a)一个请求到达过滤器filter时还未经过spring的请求解析,到达拦截器(interceptor)时请求已经经过spring的解析,而spring对multipart/form-data方式请求已做处理;

 

       b)实现了ServletRequest的org.springframework.web.multipart.MultipartHttpServletRequest.java 类 (这个类的getParameter方法可以获取到multipart/form-data和非multipart/form-data方法上传的参数。

所以我们得出一下解决方案:首先在你的spring配置文件中配置MultipartHttpServletRequest

1 <bean id="multipartResolver"  class="org.springframework.web.multipart.commons.CommonsMultipartResolver" p:defaultEncoding="UTF-8">
2     <property name="maxUploadSize">
3         <value>104857600</value>
4     </property>
5     <property name="maxInMemorySize">
6         <value>4096</value>
7     </property>
8 </bean>

或者使用:springboot注解方式定义bean

 

然后在filter初始化时注入MultipartResolver

1 // 用于创建MultipartHttpServletRequest
2 private MultipartResolver multipartResolver = null;
3      
4 @Override
5 public void init(FilterConfig arg0) throws ServletException {
6 // 注入bean
7     multipartResolver = ((MultipartResolver)ApplicationContextUtil.getContext().getBean("multipartResolver", MultipartResolver.class));
 }

最后在dofilter中追加代码:

String contentType = req.getContentType();
   if (contentType != null && contentType.contains("multipart/form-data")) {
      MultipartHttpServletRequest multipartRequest = multipartResolver.resolveMultipart((HttpServletRequest)req);
       //在这里可以通过multipartRequest 获取参数了
       // 把multipartRequest让请求继续执行,之后的所有拦截器和controller都能继续get参数           
    chain.doFilter(multipartRequest , response);       
    return;
}

然而非常不幸,老项目中使用的技术栈是Struts2,我们无法直接解决,因此只能曲线救国了;

2)首先在前端界面做手脚,form表单提交界面使用js校验,拦截住恶意的脚本(本人已测试使用,没毛病);

 1 /**
 2  * xss校验函数,返回值:true 表示存在xss漏洞,false:不存在
 3  * @param v
 4  * @returns {boolean}
 5  */
 6 function checkIsXSS(value) {
 7     var res1 = (new RegExp("\\b(document|onload|eval|script|img|svg|onerror|javascript|alert)\\b")).test(value);
 8     var res2 = (new RegExp("<","g")).test(value);
 9     var res3 = (new RegExp(">","g")).test(value);
10     return ((res1 == true) || (res2 == true) || (res3 == true));
11 }

若只是糊弄质控测试,前端做拦截就够用了;但因为暂时专业的,所以后端也是要做相关拦截滴;

后端采用切面+注解的方式,对controller控制层统一做拦截过滤,若发现请求入参中带有恶意脚本,直接将返回前端文本非法提示语;因代码本身并不复杂,此处不做赘述;

 

推荐阅读