Java代码审计 您所在的位置:网站首页 filegetoriginalfilename乱码 Java代码审计

Java代码审计

2023-03-13 22:21| 来源: 网络整理| 查看: 265

环境配置

Springboot:2.7.5

依赖

```            org.springframework.boot        spring-boot-starter-web    

       org.springframework.boot        spring-boot-starter-test        test    

       org.apache.tomcat.embed        tomcat-embed-jasper    

       commons-fileupload        commons-fileupload        1.2.2                    commons-io        commons-io        2.0.1     ```

application.yml

``` spring: mvc:   view:     prefix: /WEB-INF/jsp/     suffix: .jsp web:   resources:     static-locations: classpath:/templates/

server: port: 8081 ```

前置知识 multipart/form-data

multipart/form-data这种编码方式的表单会以二进制流的方式来处理表单数据,这种编码方式会把文件域指定文件的内容也封装到请求参数里。通常会见到配合method=post去搭配使用,而后端采取inputstream等方式读取客户端传入的二进制流来处理文件。

00截断问题

PHP中:PHP\            

上传入口

MultipartFile上传    

ServletFileUpload上传

基于Commons-FileUpload组件

依赖

   commons-fileupload    commons-fileupload    1.2.2

Springboot环境需关闭multipart

spring: servlet:   multipart:     enabled: false

创建步骤 创建磁盘工厂:DiskFileItemFactory factory = new DiskFileItemFactory(); 创建处理工具:ServletFileUpload upload = new ServletFileUpload(factory); 设置上传文件大小:upload.setFileSizeMax(3145728); 接收全部内容:List items = upload.parseRequest(request);

``` @RequestMapping("/upload3") protected void ServletFileUpload(HttpServletRequest request, HttpServletResponse response) throws IOException {   {        //设置文件上传路径        String filePath = request.getServletContext().getRealPath("upload");        File uploadFile = new File(filePath);        //若不存在该路径则创建之        if (!uploadFile.exists() && !uploadFile.isDirectory()) {            uploadFile.mkdir();       }

try {            //创建一个磁盘工厂            DiskFileItemFactory factory = new DiskFileItemFactory();            //创建文件上传解析器            ServletFileUpload fileupload = new ServletFileUpload(factory);            //三个照顾要上传的文件大小            fileupload.setFileSizeMax(3145728);            //判断是否为multipart/form-data类型,为false则直接跳出该方法            if (!fileupload.isMultipartContent(request)) {                return;           }            //使用ServletFileUpload解析器解析上传数据,解析结果返回的是一个List集合,每一个FileItem对应一个Form表单的输入项            List items = fileupload.parseRequest(request);            for (FileItem item : items) {                //isFormField方法用于判断FileItem类对象封装的数据是否属于一个普通表单字段,还是属于一个文件表单字段,如果是普通表单字段则返回true,否则返回false。                if (item.isFormField()) {                    String name = item.getFieldName();                    //解决普通输入项的数据的中文乱码问题                    String value = item.getString("UTF-8");                    String value1 = new String(name.getBytes("iso8859-1"), "UTF-8");                    System.out.println(name + " : " + value);                    System.out.println(name + " : " + value1);               } else {                    //获得上传文件名称                    String fileName = item.getName();                    System.out.println(fileName);                    if (fileName == null || fileName.trim().equals("")) {                        continue;                   }                    //注意:不同的浏览器提交的文件名是不一样的,有些浏览器提交上来的文件名是带有路径的,如: c:\a\b\1.txt,而有些只是单纯的文件名,如:1.txt                    //处理获取到的上传文件的文件名的路径部分,只保留文件名部分                    fileName = fileName.substring(fileName.lastIndexOf(File.separator) + 1);                    //获取item中的上传文件的输入流                    InputStream is = item.getInputStream();                    FileOutputStream fos = new FileOutputStream(filePath + File.separator + fileName);                    byte buffer[] = new byte[1024];                    int length = 0;                    while ((length = is.read(buffer)) > 0) {                        fos.write(buffer, 0, length);                   }                    is.close();                    fos.close();                    item.delete();               }           }            response.getWriter().write("Success!");       } catch (FileUploadException e) {            e.printStackTrace();       }   } } ```

上传入口

ServletFileUpload上传    

Servlet Part上传

Servlet3之后,有提出了request.getParts()获取上传文件的方式。

除此外若加上注解@MultipartConfig,则可定义一些上传属性

| 方法 | 类型 | 是否可选 | 作用 | | ----------------------- | ------------ | -------------- | -------------------------------------------------------------------- | | fileSizeThershold | int | 是 | 当前数据量大于该值时,内容将被写入文件 | | location | String | 是 | 存放文件的路径 | | maxFileSize | long | 是 | 允许上传的文件最大值,默认为-1,表示没有限制 | | maxRequestSize | long | 是 | 针对multipart/form-data 请求的最大数量,默认为-1,表示没有限制 |

ServletPart常用方法 String getName()  获取这部分的名称,例如相关表单域的名称 String getContentType()  如果Part是一个文件,那么将返回Part的内容类型,否则返回null(可以利用这一方法来识别是否为文件域) Collection getHeaderNames()  返回这个Part中所有标头的名称 String getHeader(String headerName)  返回指定标头名称的值 void write(String path)  将上传的文件写入服务器中项目的指定地址下,如果path是一个绝对路径,那么将写入指定的路径,如果path是一个相对路径,那么将被写入相对于location属性值的指定路径。 InputStream getInputStream()  以inputstream的形式返回上传文件的内容

@RequestMapping("/upload4") public void ServletPartUpload(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {    String filePath = request.getServletContext().getRealPath("upload");    File uploadFile = new File(filePath);    //若不存在该路径则创建之    if (!uploadFile.exists() && !uploadFile.isDirectory()) {        uploadFile.mkdir();   }    //通过表单中name属性值,获取filename    Part part = request.getPart("file");    if(part == null) {        return ;   }    String filename = filePath + File.separator + part.getSubmittedFileName();    part.write(filename);    part.delete(); }

文件上传入口

ServletPart上传    

文件上传漏洞

上述都是no waf的文件上传方式,若不做任何防御的情况下,可以实现任意文件上传,造成文件上传漏洞

通过上述任意方法,上传jsp马

执行成功

防御 content-type白名单

//1、MIME检测    String contentType = file.getContentType();    String[] white_type = {"image/gif","image/jpeg","image/jpg","image/png"};    Boolean ctFlag = false;    for (String suffix:white_type){        if (contentType.equalsIgnoreCase(suffix)){            ctFlag = true;            break;       }   }    if (!ctFlag){        return "content-type not allow";   }

如果单设置这一个的话其实很好绕过

重命名文件

可以用uuid、md5、时间戳等方式

//2、重命名文件 String uuid = UUID.randomUUID().toString(); fileName = uuid+fileName.substring(fileName.lastIndexOf("."));;

后缀白名单

//3、后缀白名单 String fileSuffix = fileName.substring(fileName.lastIndexOf(".")); String[] white_suffix = {"gif","jpg","jpeg","png"}; Boolean fsFlag = false; for (String suffix:white_suffix){    if (contentType.equalsIgnoreCase(fileSuffix)){        fsFlag = true;        break;   } } if (!fsFlag){    return "suffix not allow"; }

绕过MIME检测后,可以通过白名单进行进一步的防御

修改存储位置

可以将图片存放到不可访问的路径,例如:Servlet的WEB-INF下,默认情况是访问不到的

//4、修改存储位置 String filePath = request.getServletContext().getRealPath("/WEB-INF/upload");

最终代码

``` public String MultiFileUpload(@RequestParam("file") MultipartFile file ,HttpServletRequest request) {        if (file.isEmpty()) {            return "请上传文件";       }

//       String filePath = request.getServletContext().getRealPath("upload");        String fileName = file.getOriginalFilename();        //1、MIME检测        String contentType = file.getContentType();        String[] white_type = {"image/gif","image/jpeg","image/jpg","image/png"};        Boolean ctFlag = false;        for (String suffix:white_type){            if (contentType.equalsIgnoreCase(suffix)){                ctFlag = true;                break;           }       }        if (!ctFlag){            return "content-type not allow";       }        //2、重命名文件        String uuid = UUID.randomUUID().toString();        fileName = uuid+fileName.substring(fileName.lastIndexOf("."));;        //3、后缀白名单        String fileSuffix = fileName.substring(fileName.lastIndexOf("."));        String[] white_suffix = {"gif","jpg","jpeg","png"};        Boolean fsFlag = false;        for (String suffix:white_suffix){            if (contentType.equalsIgnoreCase(fileSuffix)){                fsFlag = true;                break;           }       }        if (!fsFlag){            return "suffix not allow";       }        //4、修改存储位置        String filePath = request.getServletContext().getRealPath("/WEB-INF/upload/");        File dest = new File(filePath + File.separator + fileName);        if (!dest.getParentFile().exists()) {            dest.getParentFile().mkdirs();       }        try {            file.transferTo(dest);            return "Success!";       } catch (IOException e) {            e.printStackTrace();       }        return "";   } ```

代码审计中常见文件上传关键字

DiskFileItemFactory @MultipartConfig MultipartFile File upload InputStream OutputStream write fileName filePath



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有