文件上传安全指南:保护免受不受限制的文件上传攻击

文件上传安全指南:保护免受不受限制的文件上传攻击

在现代应用程序中,文件上传功能是一个常见且重要的部分。然而,这也为攻击者提供了潜在的攻击向量,尤其是不受限制的文件上传攻击。本文将详细介绍如何通过一系列安全措施来保护文件上传功能,确保系统的安全性。

1. 验证用户权限

所有文件上传操作都应在服务器端对用户进行身份验证,并必须实施适当的控制措施,以防止对未通过身份验证的用户造成的拒绝服务攻击。在用户使用任何文件上传服务前,应该满足以下条件:

  • 用户应是注册用户或可识别用户,以便为其上传功能设置限制和限制。
  • 用户应具有访问或修改文件的适当权限。

示例代码

以下是一个示例,展示了如何在Java中验证用户权限:

import java.util.HashMap;
import java.util.Map;public class UserAuthService {private Map<String, User> users = new HashMap<>();public UserAuthService() {// 初始化一些示例用户users.put("user1", new User("user1", "password1", true));users.put("user2", new User("user2", "password2", false));}public boolean authenticate(String username, String password) {User user = users.get(username);return user != null && user.getPassword().equals(password);}public boolean hasPermission(String username) {User user = users.get(username);return user != null && user.hasUploadPermission();}private static class User {private String username;private String password;private boolean canUpload;public User(String username, String password, boolean canUpload) {this.username = username;this.password = password;this.canUpload = canUpload;}public String getPassword() {return password;}public boolean hasUploadPermission() {return canUpload;}}public static void main(String[] args) {UserAuthService authService = new UserAuthService();String username = "user1";String password = "password1";if (authService.authenticate(username, password)) {if (authService.hasPermission(username)) {System.out.println("用户 " + username + " 有上传权限。");} else {System.out.println("用户 " + username + " 没有上传权限。");}} else {System.out.println("用户身份验证失败。");}}
}

2. 禁止可执行文件

上传的文件必须是不可执行的,并且必须存储为一个新的文件,使用唯一的文件名,而不是用户指定的文件名。

示例代码

以下是一个示例,展示了如何在Java中防止可执行文件上传并生成唯一的文件名:

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.UUID;public class FileUploadService {private static final String UPLOAD_DIR = "/uploads";public String saveUploadedFile(byte[] fileBytes, String originalFileName) throws IOException {String fileExtension = getFileExtension(originalFileName);if (isExecutable(fileExtension)) {throw new IllegalArgumentException("不允许上传可执行文件。");}String uniqueFileName = generateUniqueFileName(fileExtension);Files.write(Paths.get(UPLOAD_DIR, uniqueFileName), fileBytes);return uniqueFileName;}private boolean isExecutable(String fileExtension) {return fileExtension.equalsIgnoreCase("exe") || fileExtension.equalsIgnoreCase("sh") || fileExtension.equalsIgnoreCase("bat");}private String getFileExtension(String fileName) {int dotIndex = fileName.lastIndexOf('.');return (dotIndex == -1) ? "" : fileName.substring(dotIndex + 1);}private String generateUniqueFileName(String fileExtension) {return UUID.randomUUID().toString() + "." + fileExtension;}public static void main(String[] args) {FileUploadService uploadService = new FileUploadService();byte[] fileBytes = "dummy content".getBytes();String originalFileName = "example.txt";try {String savedFileName = uploadService.saveUploadedFile(fileBytes, originalFileName);System.out.println("文件已保存为: " + savedFileName);} catch (IOException e) {e.printStackTrace();}}
}

3. 允许使用的扩展名

确保只使用业务关键的扩展名,不允许任何非必需的扩展类型。

示例代码

以下是一个示例,展示了如何在Java中限制允许的文件扩展名:

import java.util.Arrays;
import java.util.List;public class FileExtensionValidator {private static final List<String> ALLOWED_EXTENSIONS = Arrays.asList("jpg", "png", "txt", "pdf");public boolean isExtensionAllowed(String fileExtension) {return ALLOWED_EXTENSIONS.contains(fileExtension.toLowerCase());}public static void main(String[] args) {FileExtensionValidator validator = new FileExtensionValidator();String testExtension = "jpg";if (validator.isExtensionAllowed(testExtension)) {System.out.println("文件扩展名 " + testExtension + " 被允许。");} else {System.out.println("文件扩展名 " + testExtension + " 不被允许。");}}
}

4. 扩展名验证

确保在解码文件名之后进行验证,并设置适当的过滤器以避免已知的绕过手段,例如双扩展名和Null字节。

示例代码

以下是一个示例,展示了如何在Java中处理双扩展名和Null字节绕过:

public class FileNameSanitizer {public String sanitizeFileName(String fileName) {// 去除 Null 字节fileName = fileName.replaceAll("\0", "");// 检查双扩展名int firstDotIndex = fileName.indexOf('.');int lastDotIndex = fileName.lastIndexOf('.');if (firstDotIndex != lastDotIndex) {fileName = fileName.substring(0, firstDotIndex) + fileName.substring(lastDotIndex);}return fileName;}public static void main(String[] args) {FileNameSanitizer sanitizer = new FileNameSanitizer();String originalFileName = "example.jpg.php";String sanitizedFileName = sanitizer.sanitizeFileName(originalFileName);System.out.println("净化后的文件名: " + sanitizedFileName);}
}

5. Content-Type 验证

上传文件的 Content-Type 是由用户提供的,因此不能完全信任,因为它容易被篡改。尽管不应依赖 Content-Type 进行安全验证,但它提供了一个快速检查,以防止用户无意中上传错误类型的文件。

示例代码

以下是一个示例,展示了如何在Java中进行 Content-Type 验证:

import javax.servlet.http.Part;
import java.io.IOException;public class ContentTypeValidator {public boolean isValidContentType(Part filePart, String expectedContentType) throws IOException {String contentType = filePart.getContentType();return expectedContentType.equalsIgnoreCase(contentType);}public static void main(String[] args) throws IOException {Part mockPart = new MockPart("example.png", "image/png");ContentTypeValidator validator = new ContentTypeValidator();if (validator.isValidContentType(mockPart, "image/png")) {System.out.println("Content-Type 验证通过。");} else {System.out.println("Content-Type 验证失败。");}}
}

6. 文件签名验证

结合 Content-Type 验证,可以检查和验证文件的签名,以确保接收的文件是预期的类型。

示例代码

以下是一个示例,展示了如何在Java中进行文件签名验证:

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;public class FileSignatureValidator {private static final Map<String, String> FILE_SIGNATURES = new HashMap<>();static {FILE_SIGNATURES.put("jpg", "FFD8FF");FILE_SIGNATURES.put("png", "89504E");FILE_SIGNATURES.put("pdf", "255044");}public boolean isValidFileSignature(File file, String expectedExtension) throws IOException {String expectedSignature = FILE_SIGNATURES.get(expectedExtension.toLowerCase());if (expectedSignature == null) {return false;}String fileSignature = getFileSignature(file);return fileSignature.startsWith(expectedSignature);}private String getFileSignature(File file) throws IOException {try (FileInputStream fis = new FileInputStream(file)) {byte[] buffer = new byte[3];fis.read(buffer, 0, buffer.length);StringBuilder sb = new StringBuilder();for (byte b : buffer) {sb.append(String.format("%02X", b));}return sb.toString();}}public static void main(String[] args) throws IOException {FileSignatureValidator validator = newFileSignatureValidator();File testFile = new File("example.jpg");if (validator.isValidFileSignature(testFile, "jpg")) {System.out.println("文件签名验证通过。");} else {System.out.println("文件签名验证失败。");}}
}

7. 文件内容验证

根据预期类型,可以对特定文件内容进行验证,例如图像文件、Microsoft 文档等。

示例代码

以下是一个示例,展示了如何在Java中对图像文件进行验证:

import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;public class ImageValidator {public boolean isImageValid(File imageFile) {try {BufferedImage image = ImageIO.read(imageFile);return image != null;} catch (IOException e) {return false;}}public static void main(String[] args) {ImageValidator validator = new ImageValidator();File testImage = new File("example.jpg");if (validator.isImageValid(testImage)) {System.out.println("图像文件验证通过。");} else {System.out.println("图像文件验证失败。");}}
}

8. 文件名净化处理

应创建一个随机字符串作为文件名。文件名长度限制应考虑到存储文件的系统,因为每个系统都有自己的文件名长度限制。

示例代码

以下是一个示例,展示了如何在Java中生成随机文件名:

import java.util.UUID;public class FileNameGenerator {public String generateRandomFileName(String fileExtension) {return UUID.randomUUID().toString() + "." + fileExtension;}public static void main(String[] args) {FileNameGenerator generator = new FileNameGenerator();String randomFileName = generator.generateRandomFileName("txt");System.out.println("生成的随机文件名: " + randomFileName);}
}

9. 文件存储位置

存储文件的位置必须根据安全需求进行选择,包括:

  • 将文件存储在不同的主机上,以便将为用户提供服务的应用程序和处理文件上传及其存储的主机完全隔离。
  • 将文件存储在 webroot 之外,只允许管理员访问的目录。
  • 将文件存储在 webroot 中,并将其设置为只写权限。
  • 如果需要读访问,则必须设置适当的控制(例如,内部 IP、授权用户等)。

示例代码

以下是一个示例,展示了如何在Java中将文件存储在不同的位置:

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;public class FileStorageService {private static final String WEBROOT_STORAGE_DIR = "/webroot/uploads";private static final String NON_WEBROOT_STORAGE_DIR = "/secure/uploads";public void saveFile(byte[] fileBytes, String fileName, boolean isWebroot) throws IOException {String storageDir = isWebroot ? WEBROOT_STORAGE_DIR : NON_WEBROOT_STORAGE_DIR;Files.write(Paths.get(storageDir, fileName), fileBytes);}public static void main(String[] args) {FileStorageService storageService = new FileStorageService();byte[] fileBytes = "dummy content".getBytes();String fileName = "example.txt";try {storageService.saveFile(fileBytes, fileName, false);System.out.println("文件已保存到非webroot目录。");} catch (IOException e) {e.printStackTrace();}}
}

10. 文件系统权限

按照最小权限原则设置文件权限,确保被授权的系统用户是唯一能够读取文件的用户。

示例代码

以下是一个示例,展示了如何在Java中设置文件权限:

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.file.attribute.PosixFilePermission;
import java.nio.file.attribute.PosixFilePermissions;
import java.util.Set;public class FilePermissionService {public void setFilePermissions(String filePath) throws IOException {File file = new File(filePath);if (file.exists()) {Set<PosixFilePermission> perms = PosixFilePermissions.fromString("rw-------");Files.setPosixFilePermissions(Paths.get(filePath), perms);}}public static void main(String[] args) {FilePermissionService permissionService = new FilePermissionService();String filePath = "/secure/uploads/example.txt";try {permissionService.setFilePermissions(filePath);System.out.println("文件权限已设置。");} catch (IOException e) {e.printStackTrace();}}
}

11. 限制上传文件大小和数量

应用程序应为上传服务设置适当的大小限制,以保护文件存储容量。如果系统要提取或处理文件,还应考虑和限制文件解压后的文件大小,并使用安全的方法计算 ZIP 文件的大小。允许上传的文件数量应限制在合理的范围内(例如,每小时每用户或 IP 地址最多 8 个文件)。

示例代码

以下是一个示例,展示了如何在Java中限制上传文件的大小和数量:

import java.util.HashMap;
import java.util.Map;public class FileUploadLimiter {private static final long MAX_FILE_SIZE = 5 * 1024 * 1024; // 5MBprivate static final int MAX_FILES_PER_HOUR = 8;private Map<String, Integer> userUploadCount = new HashMap<>();public boolean canUploadFile(String userId, long fileSize) {if (fileSize > MAX_FILE_SIZE) {return false;}int currentCount = userUploadCount.getOrDefault(userId, 0);if (currentCount >= MAX_FILES_PER_HOUR) {return false;}userUploadCount.put(userId, currentCount + 1);return true;}public static void main(String[] args) {FileUploadLimiter limiter = new FileUploadLimiter();String userId = "user1";long fileSize = 4 * 1024 * 1024; // 4MBif (limiter.canUploadFile(userId, fileSize)) {System.out.println("用户 " + userId + " 可以上传文件。");} else {System.out.println("用户 " + userId + " 超过上传限制。");}}
}

12. 文件净化处理

对于从不可信来源(如通过互联网)上传的文件,可能包含潜在的可执行内容,应该进行以下处理:

  • 净化可执行代码(如 Word 文件中的宏)。
  • 使用反病毒解决方案分析潜在的恶意软件,如果发现感染,则必须拒绝文件或将其发送到隔离区。

示例代码

以下是一个示例,展示了如何在Java中使用Apache POI对Word文件进行净化处理:

import org.apache.poi.xwpf.usermodel.XWPFDocument;import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;public class DocumentSanitizer {public void sanitizeWordDocument(String inputFilePath, String outputFilePath) throws IOException {try (FileInputStream fis = new FileInputStream(inputFilePath);XWPFDocument document = new XWPFDocument(fis)) {document.removeMacros();try (FileOutputStream fos = new FileOutputStream(outputFilePath)) {document.write(fos);}}}public static void main(String[] args) {DocumentSanitizer sanitizer = new DocumentSanitizer();String inputFilePath = "example.docx";String outputFilePath = "sanitized_example.docx";try {sanitizer.sanitizeWordDocument(inputFilePath, outputFilePath);System.out.println("文档净化处理完成。");} catch (IOException e) {e.printStackTrace();}}
}

参考链接

  • OWASP: Unrestricted File Upload
  • Apache POI Documentation
  • MIME Type List

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

安全设计 | Microsoft 威胁建模工具Threat Modeling Tool安装及使用详解(文末附样例)

1. 概览 微软威胁建模工具&#xff08;Threat Modeling Tool&#xff09;是 Microsoft 安全开发生命周期 (SDL&#xff0c;Security Develop LifeCycle) 的核心要素。 当潜在安全问题处于无需花费过多成本即可相对容易解决的阶段&#xff0c;软件架构师可以使用威胁建模工具提…

linux系统防火墙开放端口命令

目录 linux相关命令参考文章1.开放端口1.1 开发单个端口1.2 一次性开放多个端口 2.保存设置3.查看所有开放的端口4.查看防火墙状态 linux相关命令参考文章 管理、设置防火墙规则&#xff08;firewalld&#xff09;: https://download.csdn.net/blog/column/8489557/137911049 i…

Springboot 自定义线程池 ThreadPoolTaskExecutor

场景 假设 需要使用多线程清理es中的历史数据 知识 参数解释&#xff1a; corePoolSize&#xff08;核心线程数&#xff09;&#xff1a;线程池中的核心线程数量&#xff0c;即使线程池处于空闲状态&#xff0c;这些核心线程也不会被销毁。maximumPoolSize&#xff08;最大…

Android 获取内外SD卡路径

方法一&#xff1a;使用Environment类和反射 获取内置SD卡路径&#xff1a; 通过Environment.getExternalStorageDirectory().getAbsolutePath()或者System.getenv("EXTERNAL_STORAGE") 获取外置SD卡路径&#xff1a; String extSdcardPath System.getenv("SEC…

打造AI虚拟伴侣 - 优化方案

第一部分:框架优化概述 1、精确定位: 构建一个高度灵活且用户友好的平台,旨在通过无缝集成多种大型语言模型(LLMs)后端,为用户创造沉浸式的角色交互体验。不仅适配电脑端,还特别优化移动端体验,满足二次元AI虚拟伴侣市场的特定需求。 2、核心功能强化: 增强后端兼容…

一起学习大模型 - 从底层了解Token Embeddings的原理(1)

文章目录 前言1. Token Embeddings简介2. 实现原理2.1 步骤2.2 伪代码2.2.1 代码2.2.2 输出示例2.2.3 代码详细解释2.2.4 实际应用 3. 选用高维向量的好处是什么3.1 捕捉语义关系3.2 处理多义词3.3 提升模型性能3.4 平滑数据稀疏性 前言 大家在使用离线或在线的Token Embeddin…

每日练习之深度优先搜索——最大的湖

最大的湖 题目描述 运行代码 #include<iostream> using namespace std; bool mp[102][102]; int sum,num; int N,M,K; int dfs(int x,int y ) {if( mp[x][y] ){mp[x][y]0;sum;dfs(x1,y);dfs(x-1,y);dfs(x,y1);dfs(x,y-1);}return 0; } int main() {cin>>N>>…

【每日一题】52.20个机器学习问题 2 (模型部署、实践流程和应用问题)

在上一篇《20个机器学习问答题》中&#xff0c;问题主要围绕机器学习的基础概念和理论知识。 这次&#xff0c;本篇内容针对机器学习的实践和应用继续提出了20个不同的问题。【点击跳转原文】 在实际应用中&#xff0c;机器学习模型的建立流程是怎样的&#xff1f; 机器学习模…

使用delphi11编写一个基于xls作为数据库的照片展示程序

1、创建xls文档可以参考前一篇博客&#xff0c;并使用wps将文档保存为2003格式xls后缀。 2、在form上面放置adoconnection、adotable、datasource、spinedit、timer、checkbox、image、4个button组件。 image的设置&#xff1a; Image1.Align : alClient; Image1.Center : Tr…

2024年,企业的人才管理怎么做?这5点是关键!

当今时代&#xff0c;各行各业都面临着激烈的竞争。这些竞争归根结底都是人才的竞争。企业若想在竞争中掌握主动权&#xff0c;实现基业长青&#xff0c;就必须努力留住人才&#xff0c;并充分发挥他们的积极性、主动性和创造性。因此&#xff0c;做好人才管理是企业实现长期可…

如何找到docker的run(启动命令)

使用python三方库进行 需要安装python解释器 安装runlike安装包 pip3 install runlike 运行命令 runlike -p <container_name> # 后面可以是容器名和容器id&#xff0c;-p参数是显示自动换行实验 使用docker启动一个jenkins 启动命令为 docker run -d \ -p 9002:80…

机器学习 - 特征监控

特征监控的定义 特征监控是机器学习模型在生产环境中持续监控输入特征的过程&#xff0c;确保输入数据特征的分布和性质与模型训练时一致&#xff0c;从而保证模型在生产环境中的表现稳定和可靠。特征监控通过检测数据的漂移、变化和异常&#xff0c;帮助识别潜在的问题并采取…

无线领夹麦克风哪个品牌音质最好,揭秘无线领夹麦哪个牌子好用

​随着社交媒体和内容创作的兴起&#xff0c;清晰可靠的音频捕捉已成为打造高品质作品的关键要素。无线领夹麦克风因其轻巧设计和用户友好的接口而受到青睐&#xff0c;它能够确保你的声音在任何环境下都能被完美捕捉。经过精心测试和对比&#xff0c;以下几款无线领夹麦克风是…

Socket CAN中ctrlmode有哪些?

在Linux中,socketcan 的 ctrlmode 是一个用于配置CAN设备控制模式的标志字段。该字段的值由一组标志位组成,这些标志位控制CAN设备的各种操作模式。以下是一些常见的 ctrlmode 标志及其含义: CAN_CTRLMODE_LOOPBACK: 描述:启用回环模式。作用:设备在发送帧的同时会接收它…

大数据学习之安装并配置maven环境

什么是Maven Maven字面意&#xff1a;专家、内行Maven是一款自动化构建工具&#xff0c;专注服务于Java平台的项目构建和依赖管理。依赖管理&#xff1a;jar之间的依赖关系&#xff0c;jar包管理问题统称为依赖管理项目构建&#xff1a;项目构建不等同于项目创建 项目构建是一…

Linux服务器自动监听Web应用接口,未响应自动重启JAVA应用脚本

近期部署了一个多台负载的应用在linux服务器&#xff0c;但总有其中的某台服务器应用会出现假死&#xff0c;导致dubbo请求出现RPC调用失败。当然主要问题肯定是程序上的某些问题导致的。但无法快速定位排查&#xff0c;所以弄个脚本自动监听接口&#xff0c;当出现未响应&…

《NoSQL数据库技术与应用》 MongoDB副本集

《NoSQL数据库技术与应用》 教学设计 课程名称&#xff1a;NoSQL数据库技术与应用 授课年级&#xff1a; 20xx年级 授课学期&#xff1a; 20xx学年第一学期 教师姓名&#xff1a; 某某老师 2020年5月6日 课题 名称 第4章 MongoDB副本集 计划学时 8课时 内容 分析 独立模式可…

第四十四天 完全背包理论 | 518.零钱兑换||

1.dp[j]含义&#xff1a;容量为j的背包&#xff0c;有一些可重复放入的物品&#xff0c;放满这个背包的最大价值 完全背包中每个物品可以使用无数次&#xff1a;遍历背包时采用正序遍历 &#xff08;对于纯完全背包问题&#xff09;先遍历物品还是先遍历背包无所谓&#xff1…

C语言——⾼位优先与低位优先的不同之处是什么?

一、问题 C语⾔的最⼤特⾊就是可移植性好。根据机器类型的不同&#xff0c;⾼位优先与低位优先也不同。那么&#xff0c;最好的可移植的 C 程序应该同时适⽤这两种类型的计算机。下⾯了解⼀下⾼位优先与低位优先的不同之处。 二、解答 所谓的⾼位优先&#xff0c;就是最低的地…