《这是知识点》之上传POST可绕过Java过滤器

《这是知识点》之上传POST可绕过Java过滤器

项目场景:

提示:公司有个项目做安全测评时发现了一个注入问题。系统设置了全局的XSS过滤器,在其他功能点上生效了,但在一个发布功能没有被过滤,后续排查发现安全测评走的是上传包注入方式,故记录此次问题


# 问题描述: 问题功能模块的作用是一个添加相关参数并上传附件的点,问题出在添加的参数未被全局XSS过滤器生效

原因分析:

基于Tomcat和Spring MVC的底层代码分析问题原因

常用的请求分文三种,GET型请求,普通POST型请求和上传POST型请求。本文的普通型POST请求指的是除上传POST型请求之外的POST请求,而上传POST型请求就是我们上传包对应的请求。

  1. Spring MVC如何获取到HTTP请求参数值?
    前端提交的请求会先到达Tomcat服务器,其解析请求参数主要在Request.parseParameters()中进行。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
//org.apache.catalina.connector.Request
protected void parseParameters() {
this.parametersParsed = true;
Parameters parameters = this.coyoteRequest.getParameters();
boolean success = false;

try {
parameters.setLimit(this.getConnector().getMaxParameterCount());
...
parameters.handleQueryParameters();
if (this.usingInputStream || this.usingReader) {
success = true;
} else if (!this.getConnector().isParseBodyMethod(this.getMethod())) {
success = true;
} else {
// 获取请求包ContentType头
String contentType = this.getContentType();
...
// 如果请求ContentType为multipart/form-data,也就是上传POST
if ("multipart/form-data".equals(contentType)) {
//对上传包进行解析
this.parseParts();
success = true;
return;
} else if (!"application/x-www-form-urlencoded".equals(contentType)) {
success = true;
return;
} else {
int len = this.getContentLength();
...
parameters.processParameters(formData, 0, len);
...
success = true;
}
}
}
}

Tomcat会根据ContentType是否为multipart/form-data判断是否问上传POST型请求,若是则会调用parseParts()来解析,我们继续跟进。由于allowCasualMultipartParsing配置项默认为false,parseParts()直接就返回了,也就是说Tomcat默认不会解析上传POST请求。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
private void parseParts() {
if (this.parts == null && this.partsParseException == null) {
MultipartConfigElement mce = this.getWrapper().getMultipartConfigElement();
if (mce == null) {
/*
Tomcat7.0+ 已经内置了multipart支持,但是必须显示激活,默认关闭。在全局tomcat配置文件context.xml,或者为war的本地context.xml添加<Context allowCasualMultipartParsing="true">开启。
*/
if (!this.getContext().getAllowCasualMultipartParsing()) {
this.parts = Collections.emptyList();
return;
}
...
}
...
}
}

对针对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层获取。

  1. 上传包无法被过滤的原理
    经过跟踪发现,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过滤器/

# JAVA

评论

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×