java过滤器获取上传的文件_【上传专题】服务器端过滤及其绕过方式

本文介绍了Java服务器端文件上传时的过滤方法,包括Content-Type检测、文件扩展名检测和文件头检测,分析了各种过滤方式的隐患,并提出了文件加载检测的重要性,强调了安全的上传机制。
摘要由CSDN通过智能技术生成

上一篇文章,讲述了如何绕过前端文件类型。

1、引言

这一篇讲述一些常见的服务端过滤方式,以及各种过滤方式存在的隐患。并给出怎样处理服务端和前端过滤,以达到更加安全的上传机制。

2、本文大纲

1)Content-Type(Mime Type)检测过滤,以及如何绕过;

2)文件扩展名检测;

3)文件头检测;

4)文件加载检测。

3、Content-Type 检测过滤

按照正常的上传方式,会根据上传的文件类型,指定Content-Type类型,例如:jpg文件对应的Content-Type是p_w_picpath/jpeg;

见下例:package com.fileupload.servlets;

import java.io.File;

import java.io.IOException;

import java.io.PrintWriter;

import java.util.List;

import javax.servlet.ServletContext;

import javax.servlet.ServletException;

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;

public class UploadFilterExtServlet extends HttpServlet {

public void doPost(HttpServletRequest request, HttpServletResponse response)

throws ServletException, IOException {

if (!ServletFileUpload.isMultipartContent(request)) {

response.getWriter().print("NOT MultiPart Request");

return;

}

String webPath = this.getServletContext().getRealPath("");

DiskFileItemFactory factory = new DiskFileItemFactory();

factory.setSizeThreshold(1024 * 1024);

factory.setRepository(new File(webPath + File.separator + "tmp")); // 临时仓库

ServletFileUpload fileUpload = new ServletFileUpload(factory);

fileUpload.setFileSizeMax(1024 * 1024 * 5);

fileUpload.setSizeMax(1024 * 1024 * 6);

fileUpload.setHeaderEncoding("utf-8");

try {

List fileItems = fileUpload.parseRequest(request);

for (FileItem fileItem : fileItems) {

String fieldName = fileItem.getFieldName();  // 字段名称

String name = fileItem.getName();                      // 如果是表单字段,那么为空;否则为文件名

String contentType = fileItem.getContentType(); // 获取上传文件的Content-Type类型

if (!fileItem.isFormField()) { // 非表单字段,即上传文件

File file = new File(webPath + File.separator + "upImage" + File.separator + name);

if (!file.getParentFile().exists()) {

file.mkdir();

}

if (contentType.equalsIgnoreCase("p_w_picpath/jpeg")) {

fileItem.write(file);

}else {

if (file.exists() &&  file.isFile()) {

fileItem.delete();

response.getWriter().print("Invalid File.");

}

}

}

}

} catch (FileUploadException e) {

e.printStackTrace();

} catch (Exception e) {

e.printStackTrace();

}

}

}

cdeac9badaac03a8d68c78083c717e15.png

在修改Content-Type之后,服务端将认为此次上传是合法,因此也就绕过了Content-Type的限制。

4、文件扩展名检测

如果在java中使用文件扩展名,并不存在0x00截断的问题,但是如果是asp那么会出现0x00文件截断问题,例如:上传test.txt.jpg 将.修改为0x00,那么系统会认为test.txt才是其文件名称,具体这里不做介绍,但是作为一种相对简单还是有一定效果的检测方式,文件扩展名检测一般是必须的。但是并不代表其是种安全的依靠。

简单而言,我们可以修改文件名以jpg后缀即可,也就可以上传非法文件了。if (contentType.endsWith("jpg")) { // 将3中代码,判断content-type修改为判断jpg后缀

fileItem.write(file);

}

如下图:

533c023478da47e5191578ae5d840892.png

那么同样可以上传非图片的文件,可能你会认为上传在服务器上的该文件,已经命名为jpg文件,顶多无法显示,如果你这样想就大错特错,因为可以将非法的脚本嵌入到文件中。并且文件名扩展的检测一般使用白名单比较好,因为黑名单难免会有遗漏,一旦遗漏了,也可能会有致命的问题。

5、文件头检测

通常一个文件会有一种标识,即表明该文件的类型。因此采用4中的方式上传一个txt文件,虽然其绕过了后缀名的检测,但是此时我们可以对该文件进行检测,初步判定该文件是否是jpg文件,也就是通过文件头来判定。

文件头一般是一个文件的开头字节内容,如下代码,展示java获取文件头的方式:package com.fileupload.types;

import java.io.File;

import java.io.FileInputStream;

import java.io.IOException;

import java.util.HashMap;

import java.util.Map;

import org.apache.commons.codec.binary.Hex;

/**

*  判断文件头类型是否合法

* @author wangzp

*

*/

public class FileType {

public final static String JPG = "FFD8FF";

public final static String PNG = "89504E47";

public final static String GIF = "47494638";

public final static String BMP = "424D";

public final static Map fileTypes = new HashMap();

static {

fileTypes.put("jpg", JPG);

fileTypes.put("png", PNG);

fileTypes.put("gif", GIF);

fileTypes.put("bmp", BMP);

}

/**

* 获取文件头

* @param filepath

* @return

* @throws IOException

*/

public static String getFileHeader(File file) throws IOException {

FileInputStream input = new FileInputStream(file);

byte[] buffer = new byte[4];

input.read(buffer, 0, buffer.length);

input.close();

return new String(Hex.encodeHex(buffer));

}

/**

* 验证文件头类型是否合法

* @param fileType

* @param file

* @return

* @throws IOException

*/

public static boolean isValidFile(String fileType, File file) throws IOException {

String fileHeader = getFileHeader(file);

String fileTypeHeader = fileTypes.get(fileType);

if (fileHeader == null || fileTypeHeader == null) {

return false;

}

if (fileHeader.startsWith(fileTypeHeader)) {

return true;

}

return false;

}

}

由此,我们可以在上传之后判断该文件是否是合法文件,如下代码展示:if (name.endsWith("jpg")) {

fileItem.write(file);

if (!FileType.isValidFile("jpg", file)) {

//fileItem.delete();

file.delete();

return;

}else {

response.getWriter().print("Invalid File Header.");

return;

}

}

代码有点粗糙,但基本可以展示出使用test.txt伪装的jpg文件,是无法上传成功的;但是这不是绝对的,因此可以在图片中加入虚假的文件头。那么面对这种情况,该如何解决呢?接下来将使用文件加载检测。

6、文件加载检测

文件加载实际上是对文件的预览方式,可以分为一次渲染和二次渲染;一般而言二次渲染后的图片很难攻入,很难在图片中嵌入代码,因此个人建议使用二次渲染,至少应该一次渲染,如果渲染失败,可以认为该文件是非法文件。不让其上传。

在本文中,不介绍文件加载检测过程,将在后续文章中介绍图片渲染的方法。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值