项目场景:
提示:公司有个项目做安全测评时发现了一个注入问题。系统设置了全局的XSS过滤器,在其他功能点上生效了,但在一个发布功能没有被过滤,后续排查发现安全测评走的是上传包注入方式,故记录此次问题
# 问题描述: 问题功能模块的作用是一个添加相关参数并上传附件的点,问题出在添加的参数未被全局XSS过滤器生效
原因分析:
基于Tomcat和Spring MVC的底层代码分析问题原因
常用的请求分文三种,GET型请求,普通POST型请求和上传POST型请求。本文的普通型POST请求指的是除上传POST型请求之外的POST请求,而上传POST型请求就是我们上传包对应的请求。
- Spring MVC如何获取到HTTP请求参数值?
前端提交的请求会先到达Tomcat服务器,其解析请求参数主要在Request.parseParameters()中进行。
1 | //org.apache.catalina.connector.Request |
Tomcat会根据ContentType是否为multipart/form-data判断是否问上传POST型请求,若是则会调用parseParts()来解析,我们继续跟进。由于allowCasualMultipartParsing配置项默认为false,parseParts()直接就返回了,也就是说Tomcat默认不会解析上传POST请求。
1 | private void parseParts() { |
对针对GET行请求和普通POST,Tomcat会调用parameters.processParameters()方法来解析,接下来Spring MVC会收到Tomcat传来的HttpServletRequest,此时若请求为上传POST型,Spring MVC会继续调用commons-fileuplad.jar对Tomcat传来的原生Servlet请求类HttpServletRequest的实例进行解析处理。
Spring MVC将原生的HttpServletRequest对象传入CommonsMultipartResolver类的parseRequest()方法进行解析处理。
CommonsMultipartResolver.parseRequest()方法主要分两步对上传请求进行解析。
第一步,调用commons-fileupload.jar中的ServletFileUpload类的parseRequest()方法来解析出保存有上传表单各个元素的FileItem列表。
第二步,调用CommonsFileUploadSupport.parseFileItem()方法解析FileItem列表为保存有表单字段名,字段值等信息MultipartParsingResult类型的Map。
最后将上传表单解析的所有元素(multipartFiles,multipartParameters,multipartParameterContentTypes)封装为一个MultipartParsingResult并返回。至此上传POST型请求的解析工作完成。
下面我们来看下这两步的执行细节。首先第一步最终的处理方法为FileUploadBase.parseRequest(),FileUploadBase.parseRequest()解析完会返回一个FileItem实例列表。FileItem就是存储着上传表单的各种元素(字段名,ContentType,是否是简单表单字段,文件名。),接着来到第二步,调用CommonsFileUploadSupport.parseFileItem()对commons-fileupload.jar处理的结果—FileItem列表,进行处理。最后将上传表单解析的所有元素(multipartFiles,multipartParameters,multipartParameterContentTypes)封装为一个MultipartParsingResult并返回。至此上传POST型请求的解析工作完成。
最后Spring MVC,会使用HandlerMethodInvoker.resolveRequestParam()方法,将解析好的请求参数的值,绑定到不同的对象上,方便Controller层获取。
- 上传包无法被过滤的原理
经过跟踪发现,Spring MVC对各类型请求参数的解析并实现自动绑定,主要在HandlerMethodInvoker.resolveRequestParam()方法。
通过调式发现,这里如果是GET型和普通POST型请求的话,getRequest()获取到的对象是我们编写的过滤类XssHttpServletRequestWrapper的实例,故调用该对象getParameterValues()来获取值,自然是被过滤了!
若是上传POST行请求的话,getRequest()获取到的是CommonsMultipartResolver类的对象。但实际上调用该对象的getParamterValues()方法,会执行到DefaultMultipartHttpServletRequest类的getParamterValues()类获取值。上传包中的参数值没有被过滤,是因为Spring MVC在解析上传包获取其参数值时,没有使用我们编写的过滤类XssHttpServletRequestWrapper中的getParamterValues()方法,而是使用了DefaultMultipartHttpServletRequest类getParamterValuses()。
最后特别说明一点,其实上传POST请求数据是流经过过滤器的。没有被过滤,是由于获取参数值的时候,没有调用过滤器Wrapper对象的方法。所以最终我们看到了上传包可以“绕过”过滤器检查的现象。
另外,实际开发中,文件上传的文件包上传最好对文件头进行分析,判断文件类型,不单单的对文件后缀做判断,常见的文件类型的文件头是可预见的。
关注Github:1/2极客
关注博客:御前提笔小书童
关注网站:开发者的花花世界
关注公众号:开发者的花花世界
本作品采用知识共享署名 4.0 中国大陆许可协议进行许可,欢迎转载,但转载请注明来自御前提笔小书童,并保持转载后文章内容的完整。本人保留所有版权相关权利。
本文链接:https://royalscholar.cn/2021/01/30/《这是知识点》之上传POST可绕过Java过滤器/