【Java代码审计 | 第七篇】文件上传漏洞成因及防范

未经许可,不得转载。

文章目录

    • 文件上传漏洞
    • 漏洞成因
      • 未验证文件类型和扩展名
      • 未限制文件上传路径
    • 防范
      • 验证文件类型和扩展名
      • 验证文件内容
      • 限制文件上传路径
      • 使用安全的文件上传库
    • 标准代码

在这里插入图片描述

文件上传漏洞

文件上传漏洞是指攻击者通过上传恶意文件(如可执行脚本、病毒、木马等)到服务器,从而执行恶意操作或获取服务器控制权的安全漏洞,一般发生在应用程序未对上传的文件进行严格的验证和限制时。

漏洞成因

未验证文件类型和扩展名

import java.io.File;
import java.io.IOException;
import java.util.List;
import javax.servlet.ServletException;
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.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;

@WebServlet("/upload") // 指定Servlet的URL映射
public class FileUploadServlet extends HttpServlet {
    
    // 处理POST请求,实现文件上传
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 检查请求是否为多部分(即文件上传请求)
        if (ServletFileUpload.isMultipartContent(request)) {
            // 创建磁盘文件项工厂,用于处理文件上传
            DiskFileItemFactory factory = new DiskFileItemFactory();
            ServletFileUpload upload = new ServletFileUpload(factory);

            try {
                // 解析请求,获取文件项列表
                List<FileItem> items = upload.parseRequest(request);
                
                for (FileItem item : items) {
                    // 检查是否为文件字段,而不是普通表单字段
                    if (!item.isFormField()) {
                        // 获取上传文件的文件名
                        String fileName = new File(item.getName()).getName();
                        // 定义服务器上的文件存储路径(此处为/uploads/目录)
                        String filePath = "/uploads/" + fileName;
                        // 将上传的文件写入到服务器指定路径
                        item.write(new File(filePath));
                        // 向客户端返回上传成功的信息
                        response.getWriter().println("File uploaded: " + fileName);
                    }
                }
            } catch (Exception e) {
                e.printStackTrace(); // 打印异常信息,方便调试
            }
        }
    }
}

问题:未验证文件类型和扩展名,攻击者可以上传任意文件(如 .jsp.exe 等)。

未限制文件上传路径

String filePath = "/uploads/" + fileName;
item.write(new File(filePath));

问题:文件上传路径未做限制,攻击者可以通过构造特殊文件名(如 ../../malicious.jsp)将文件上传到任意目录。

防范

验证文件类型和扩展名

  • 使用白名单机制,只允许上传指定的文件类型(如 .jpg.png.pdf 等)。
  • 不要依赖客户端验证(如 HTML 的 accept 属性),必须在服务器端进行验证。
String[] allowedExtensions = { "jpg", "png", "pdf" };
String fileExtension = fileName.substring(fileName.lastIndexOf(".") + 1).toLowerCase();

boolean isValidExtension = false; //默认为不合法类型
for (String ext : allowedExtensions) {
    if (ext.equals(fileExtension)) {
        isValidExtension = true; //若匹配到白名单,则合法。
        break;
    }
}

if (!isValidExtension) {
    response.getWriter().println("Invalid file type.");
    return;
}

验证文件内容

使用工具(如 Apache Tika)验证文件内容是否与扩展名匹配。

import org.apache.tika.Tika;

Tika tika = new Tika();
String detectedType = tika.detect(new File(filePath));

if (!detectedType.startsWith("image/")) {
    response.getWriter().println("Invalid file content.");
    new File(filePath).delete(); // 删除非法文件
    return;
}

限制文件上传路径

将文件上传路径限制在特定目录,避免攻击者通过路径遍历上传文件到任意目录。

String uploadDir = "/uploads/";
String filePath = uploadDir + randomFileName;

// 确保路径在允许的目录内
if (!filePath.startsWith(uploadDir)) {
    response.getWriter().println("Invalid file path.");
    return;
}

使用安全的文件上传库

使用经过验证的文件上传库(如 Apache Commons FileUpload),并确保库的版本是最新的。

标准代码

import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.tika.Tika;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.UUID;

@WebServlet("/upload")
public class FileUploadServlet extends HttpServlet {
    private static final long MAX_FILE_SIZE = 10 * 1024 * 1024; // 10MB
    private static final String UPLOAD_DIRECTORY = "/uploads"; // 上传目录
    private static final String[] ALLOWED_EXTENSIONS = { "jpg", "jpeg", "png", "pdf" }; // 允许的文件扩展名

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 检查是否为文件上传请求
        if (!ServletFileUpload.isMultipartContent(request)) {
            response.getWriter().println("Request does not contain upload data.");
            return;
        }

        // 配置上传参数
        DiskFileItemFactory factory = new DiskFileItemFactory();
        ServletFileUpload upload = new ServletFileUpload(factory);
        upload.setSizeMax(MAX_FILE_SIZE); // 设置最大文件大小

        try {
            // 解析请求
            List<FileItem> items = upload.parseRequest(request);

            for (FileItem item : items) {
                if (!item.isFormField()) {
                // 判断当前的 FileItem 是否是一个普通的表单字段,如果不是,则执行后续的文件上传处理逻辑。


                    // 获取文件名
                    String fileName = new File(item.getName()).getName();
                    String fileExtension = fileName.substring(fileName.lastIndexOf(".") + 1).toLowerCase();

                    // 验证文件扩展名
                    if (!isAllowedExtension(fileExtension)) {
                        response.getWriter().println("Invalid file type. Allowed types: " + String.join(", ", ALLOWED_EXTENSIONS));
                        return;
                    }

                    // 生成随机文件名
                    String randomFileName = UUID.randomUUID().toString() + "." + fileExtension;
                    String uploadPath = getServletContext().getRealPath("") + File.separator + UPLOAD_DIRECTORY;
                    File uploadDir = new File(uploadPath);

                    // 创建上传目录(如果不存在)
                    if (!uploadDir.exists()) {
                        uploadDir.mkdir();
                    }

                    // 保存文件
                    String filePath = uploadPath + File.separator + randomFileName;
                    File storeFile = new File(filePath);
                    item.write(storeFile);

                    // 验证文件内容
                    if (!isValidFileContent(storeFile, fileExtension)) {
                        response.getWriter().println("Invalid file content.");
                        storeFile.delete(); // 删除非法文件
                        return;
                    }

                    response.getWriter().println("File uploaded successfully: " + randomFileName);
                }
            }
        } catch (Exception e) {
            response.getWriter().println("Error occurred: " + e.getMessage());
        }
    }

    /**
     * 检查文件扩展名是否合法
     */
    private boolean isAllowedExtension(String fileExtension) {
        for (String ext : ALLOWED_EXTENSIONS) {
            if (ext.equalsIgnoreCase(fileExtension)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 验证文件内容是否合法
     */
    private boolean isValidFileContent(File file, String expectedExtension) throws IOException {
        Tika tika = new Tika();
        String detectedType = tika.detect(file);

        // 根据文件扩展名验证内容类型
        switch (expectedExtension.toLowerCase()) {
            case "jpg":
            case "jpeg":
                return detectedType.equals("image/jpeg");
            case "png":
                return detectedType.equals("image/png");
            case "pdf":
                return detectedType.equals("application/pdf");
            default:
                return false;
        }
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/984831.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【无人机路径规划】基于麻雀搜索算法(SSA)的无人机路径规划(Matlab)

效果一览 代码获取私信博主基于麻雀搜索算法&#xff08;SSA&#xff09;的无人机路径规划&#xff08;Matlab&#xff09; 一、算法背景与核心思想 麻雀搜索算法&#xff08;Sparrow Search Algorithm, SSA&#xff09;是一种受麻雀群体觅食行为启发的元启发式算法&#xff0…

狮子座大数据分析(python爬虫版)

十二星座爱情性格 - 星座屋 首先找到一个星座网站&#xff0c;作为基础内容&#xff0c;来获取信息 网页爬取与信息提取 我们首先利用爬虫技术&#xff08;如 Python 中的 requests 与 BeautifulSoup 库&#xff09;获取页面内容。该页面&#xff08;xzw.com/astro/leo/&…

DeepSeek教我写词典爬虫获取单词的音标和拼写

Python在爬虫领域展现出了卓越的功能性&#xff0c;不仅能够高效地抓取目标数据&#xff0c;还能便捷地将数据存储至本地。在众多Python爬虫应用中&#xff0c;词典数据的爬取尤为常见。接下来&#xff0c;我们将以dict.cn为例&#xff0c;详细演示如何编写一个用于爬取词典数据…

AI智能导航站HTML5自适应源码帝国cms7.5模板

源码名称&#xff1a;AI导航站HTML5自适应源码帝国cms7.5模板 开发环境&#xff1a;帝国cms 7.5 安装环境&#xff1a;phpmysql var code "4d33ef8e-9e38-43b9-b37b-38f75944ecc9" 带软件采集&#xff0c;可以挂着自动采集发布&#xff0c;无需人工操作&#xff0…

【贪心算法】将数组和减半的最小操作数

1.题目解析 2208. 将数组和减半的最少操作次数 - 力扣&#xff08;LeetCode&#xff09; 2.讲解算法原理 使用当前数组中最大的数将它减半&#xff0c;&#xff0c;直到数组和减小到一半为止&#xff0c;从而快速达到目的 重点是找到最大数&#xff0c;可以采用大根堆快速达到…

Apache XTable:在数据湖仓一体中推进数据互作性

Apache XTable 通过以多种开放表格式提供对数据的访问&#xff0c;在增强互作性方面迈出了一大步。移动数据很困难&#xff0c;在过去&#xff0c;这意味着在为数据湖仓一体选择开放表格式时&#xff0c;您被锁定在该选择中。一个令人兴奋的项目当在数据堆栈的这一层引入互作性…

hive面试题--left join的坑

student 表&#xff1a; 课程表course: 1、key为null, 不关联 select * from student s left join course c on s.id c.s_id;2、on中过滤条件 与 where 过滤条件区别 on and c.id<>‘1001’ 先过滤右表数据&#xff0c;然后与左表关联 select * from student s le…

2路模拟量同步输出卡、任意波形发生器卡—PCIe9100数据采集卡

品牌&#xff1a;阿尔泰科技 型号&#xff1a; PCIe9100、PCIe9101、PXIe9100、PXIe9101 产品系列&#xff1a;任意波形发生器 支持操作系统&#xff1a;XP、Win7、Win8、Win10 简要介绍&#xff1a; 910X 系列是阿尔泰科技公司推出的 PCIe、PXIe 总线的任意波形发生器&…

elementUI改样式失败问题——DatePicker 日期选择器

今天做一个vue2的项目时&#xff0c;发现使用deep对时间选择器的选择控件不生效&#xff0c;因为elementUI官方文档里写了&#xff1a; popper-classDatePicker 下拉框的类名 并且通过浏览器可以发现&#xff0c;选择控件是直接挂在body下的&#xff0c;所以解决方法是直接找到…

C++ 链表List使用与实现:拷贝交换与高效迭代器细致讲解

目录 list的使用&#xff1a; 构造与赋值 元素访问 修改操作 容量查询 链表特有操作 拼接&#xff08;Splice&#xff09; C11 新增方法 注意&#xff1a; stl_list的模拟实现&#xff1a; 一、链表节点设计的艺术 1.1 结构体 vs 类的选择 二、迭代器实现的精髓 2…

【C++】C++入门基础

C&#xff08;C plus plus&#xff09; 是一种计算机高级程序设计语言&#xff0c;既可以进行 C语言 的过程化程序设计&#xff0c;又可以进行以抽象数据类型为特点的基于对象的程序设计&#xff0c;还可以进行以继承和多态为特点的面向对象的程序设计。 文章目录 前言一、C 的…

探索AI对冲基金:开源自动化交易系统的革新之路

在量化交易领域,人工智能技术的应用正悄然改变传统对冲基金的运作模式。GitHub上的开源项目ai-hedge-fund为开发者和金融从业者提供了一个独特的实践平台。该项目通过多智能体系统架构,整合市场数据分析、量化策略生成、风险管理和投资组合优化等核心功能,实现了从数据采集到…

C语言每日一练——day_3(快速上手C语言)

引言 针对初学者&#xff0c;每日练习几个题&#xff0c;快速上手C语言。第三天。&#xff08;会连续更新&#xff09; 采用在线OJ的形式 什么是在线OJ&#xff1f; 在线判题系统&#xff08;英语&#xff1a;Online Judge&#xff0c;缩写OJ&#xff09;是一种在编程竞赛中用…

SpringCloud系列教程(十三):Sentinel流量控制

SpringCloud中的注册、发现、网关、服务调用都已经完成了&#xff0c;现在就剩下最后一部分&#xff0c;就是关于网络控制。SpringCloud Alibaba这一套中间件做的非常好&#xff0c;把平时常用的功能都集成进来了&#xff0c;而且非常简单高效。我们下一步就完成最后一块拼图Se…

VMware安装欧拉操作系统(openEuler)第二节

摘要&#xff1a; 本篇文章接上篇《VMware安装欧拉操作系统&#xff08;openEuler&#xff09;第一节》&#xff0c;上一篇写到vmware workstation 17中创建openEuler虚拟机&#xff0c;本篇将详细介绍openEuler操作系统初始化以及相关配置的详细内容。 VMware安装欧拉操作系统…

[数据结构]并查集--C++版本的实现代码

目录 并查集的基本框架 查找一个元素在哪一个集合 判断两个元素是否在同一个集合 将两个集合进行合并 查询有多少组 测试 大学班级的同学会来自于五湖四海&#xff0c;每个人的家乡可能都不相同&#xff0c;那么如何将相同省份的同学连接到一块&#xff0c;也就是按省份进…

基于SpringBoot+Vue的瑜伽课体验课预约系统【附源码】

基于SpringBootVue的瑜伽课体验课预约系统 一、系统技术说明二、运行说明三、系统的演示四、系统的核心代码演示 一、系统技术说明 框架&#xff1a;SpringbootVue 数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09; 数据库工具&#xff1a;Navicat11 开发软…

【编译器】VSCODE烧录ESP32-C3——xiaozhi智能聊天机器人固件

【编译器】VSCODE烧录ESP32-C3——xiaozhi智能聊天机器人固件 文章目录 [TOC](文章目录) 前言一、方法一&#xff1a;使用固件烧录工具1. 安装CH340驱动2. 打开FLASH_DOWNLOAD文件3. 选择芯片类型和烧录方式4. 选择烧录文件5. 参数配置 二、方法二&#xff1a;VSCODE导入工程1.…

【C++】 —— 笔试刷题day_1

为了锻炼自己写代码的思路&#xff0c;开始每日刷题&#xff0c;加油&#xff01;&#xff01;&#xff01; 第一题 数字统计 题目要求&#xff1a; ​ 给定一个范围 [L , R] 求出数字L在该区间内出现的次数。&#xff08;其中1<L<R<10000&#xff09; 算法思路&…

R语言和RStudio安装

整体还是比较简单的&#xff0c;主要是记录个流程。 官方镜像站列表R语言官网 1 安装R&#xff08;2025/3/6&#xff09; R语言官网&#xff1a;The R Project for Statistical Computing 打开之后就Hello world一下吧 配置环境变量 2 安装RStudio 下载地址&#xff1a;htt…