tomcat文件上传tips
简单梳理一下tomcat中文件上传的一些tips。
环境说明:
- apache-tomcat-8.5.83
- java version “1.8.0_152”
- commons-fileupload 1.4
没有设置黑白名单
我们首先使用commons.fileupload,写一个上传器servlet
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 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68
| package org.f19t.java_upload;
import java.io.File; import java.io.IOException; import java.util.Arrays; import java.util.Iterator; import java.util.List; import javax.servlet.ServletException; import javax.servlet.annotation.MultipartConfig; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.commons.fileupload.FileItem; import org.apache.commons.fileupload.FileUploadException; import org.apache.commons.fileupload.disk.DiskFileItemFactory; import org.apache.commons.fileupload.servlet.ServletFileUpload;
@WebServlet("/FileUploadServlet") @MultipartConfig(fileSizeThreshold = 1024 * 1024 * 2, // 2MB maxFileSize = 1024 * 1024 * 10, // 10MB maxRequestSize = 1024 * 1024 * 50) public class FileUploadServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
@Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { if (ServletFileUpload.isMultipartContent(request)) { DiskFileItemFactory factory = new DiskFileItemFactory(); ServletFileUpload fileUpload = new ServletFileUpload(factory); String rootPath = request.getServletContext().getRealPath("/");
try { List<FileItem> items = fileUpload.parseRequest(request); Iterator<FileItem> iter = items.iterator(); while (iter.hasNext()) { FileItem item = iter.next(); if (!item.isFormField()) { String fileName = new File(item.getName()).getName(); String filePath = rootPath + File.separator + fileName; File uploadedFile = new File(filePath); item.write(uploadedFile); response.setCharacterEncoding("utf-8"); response.getWriter().write("File uploaded successfully: " + rootPath + fileName); } }
} catch (FileUploadException e) { response.getWriter().write("File upload failed."); e.printStackTrace(); } catch (Exception e) { response.getWriter().write("Exception: " + e.getMessage()); e.printStackTrace(); } } else { response.getWriter().write("Only multipart/form-data requests are accepted"); } } }
|
这里学到一点,下方代码可以获取当前webapp的网站根目录。
1
| request.getServletContext().getRealPath("/");
|
我们再写一个html提交表单
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>File Upload Example</title> </head> <body> <form action="FileUploadServlet" method="post" enctype="multipart/form-data"> <label for="file">Choose a file to upload:</label><br> <input type="file" id="file" name="file"><br><br> <input type="submit" value="Upload"> </form> </body> </html>
|
可以发现成功上传,因为我们没有设置黑白名单。
QP编码
因为commons-fileupload1.3版本开始可以使用QP编码,所以我们可以使用QP编码去绕过一些正则表达式的匹配。
设置黑名单禁止上传jsp
我们写一个BlacklistFileItemFactory工厂类,它扩展了DiskFileItemFactory,并在创建文件项对象时检查文件类型。
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
| package org.f19t.java_upload;
import org.apache.commons.fileupload.FileItem; import org.apache.commons.fileupload.FileUploadException; import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import java.util.List;
public class BlacklistFileItemFactory extends DiskFileItemFactory { private List<String> blacklist;
public BlacklistFileItemFactory(List<String> blacklist) { this.blacklist = blacklist; }
@Override public FileItem createItem(String fieldName, String contentType, boolean isFormField, String fileName) { if (!isFormField && fileName != null) { for (String ext : blacklist) { if (fileName.endsWith(ext)) { try { throw new FileUploadException("File type not allowed."); } catch (FileUploadException e) { throw new RuntimeException(e); } } } } return super.createItem(fieldName, contentType, isFormField, fileName); } }
|
我们在文件上传servlet的doPost() 里面添加fileUpload.setFileItemFactory(new BlacklistFileItemFactory(Arrays.asList(“.jsp”)));
然后我们再次进行上传:即使我们使用QP编码,也还是可以被检测出来。
禁止上传jsp但存在目录穿越
为了产生目录穿越漏洞,我们将之前写的上传servlet的doPost() 里面修改:
- String fileName = new File(item.getName()).getName(); //被修改
- String fileName = item.getName(); //修改成
这样我们上传一次目录穿越的jsp试一下。发现我们可以传到其他的目录里面,
上传jar
tips:
Servlet 3.0 协议规范中,包含在 Jar 文件 /META-INF/resources/ 路径下的资源可以直接被web访问。
所以这里我们传一个jar包到WEB-INF/lib里面,这里我们新建一个jar包
我们通过修改数据包,将jar传到lib文件夹下
加载jar
在 $TOMCAT_HOME/conf/context.xml 文件配置下,记录了一些配置文件路径。
如果这些配置文件变动,就会进行重新加载这些jar包。
我的web.xml文件内容默认如下:
1 2 3 4 5 6
| <?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version="4.0"> </web-app>
|
然后我填加了一个人畜无害的
1
| welcome-file>upload1.html</welcome-file>
|
总的新的xml如下:
1 2 3 4 5 6 7 8 9
| <?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version="4.0"> <welcome-file-list> <welcome-file>upload1.html</welcome-file> </welcome-file-list> </web-app>
|
因为我们上传文件属于覆盖,所以这个操作的危险性是非常高的,实战不推荐使用。因为我们并不能保证可以给他恢复完整。,并且业务如果做了一些自己的配置,我们没办法给他恢复。
覆盖完成之后我们就可以访问上传jar包里面的myjar.jsp了。
参考文章: