-
四重验证机制:
- 文件扩展名检查(.xlsx/.xls)
- MIME类型检查
- 文件魔数验证(真实文件类型)
- 可执行文件特征检测
-
防御措施:
- 使用
try-with-resources
确保流关闭 - 限制文件大小防止DoS攻击
- 使用Apache POI的
FileMagic
进行专业验证
- 使用
-
生产环境建议:
Yaml
# application.yml配置 spring: servlet: multipart: max-file-size: 10MB max-request-size: 10MB
完整代码:
java
import org.apache.poi.poifs.filesystem.FileMagic;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
// 文件上传限制-只允许上传excel文件,且检查不能是脚本或者有害文件或可行性文件
public class ExcelFileValidator {
// 允许的Excel文件MIME类型
private static final String[] ALLOWED_MIME_TYPES = {
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", // .xlsx
"application/vnd.ms-excel" // .xls
};
// 最大文件大小(10MB)
private static final long MAX_FILE_SIZE = 10 * 1024 * 1024;
/**
* 验证Excel文件安全性
*
* @param file 上传的文件
* @throws IOException 文件读取异常
* @throws IllegalArgumentException 文件非法时抛出
*/
public static void validateExcelFile(MultipartFile file) throws IOException, IllegalArgumentException {
// 基础检查
if (file == null || file.isEmpty()) {
throw new IllegalArgumentException("请选择要上传的文件");
}
// 检查文件大小
if (file.getSize() > MAX_FILE_SIZE) {
throw new IllegalArgumentException("Excel文件大小不能超过10MB");
}
// 检查文件扩展名
String originalFilename = file.getOriginalFilename();
if (originalFilename == null ||
(!originalFilename.toLowerCase().endsWith(".xlsx") &&
!originalFilename.toLowerCase().endsWith(".xls"))) {
throw new IllegalArgumentException("仅支持.xlsx或.xls格式的Excel文件");
}
// 检查MIME类型
String contentType = file.getContentType();
if (contentType == null || !Arrays.asList(ALLOWED_MIME_TYPES).contains(contentType.toLowerCase())) {
throw new IllegalArgumentException("非法的Excel文件类型");
}
// 使用POI检查文件魔数(真实文件类型)
try (InputStream inputStream = file.getInputStream()) {
FileMagic fileMagic = FileMagic.valueOf(inputStream);
if (fileMagic != FileMagic.OLE2 && fileMagic != FileMagic.OOXML) {
throw new IllegalArgumentException("非法的Excel文件格式");
}
// 基础恶意内容检查
checkForExecutableContent(inputStream);
}
}
/**
* 检查是否包含可执行文件特征
*/
private static void checkForExecutableContent(InputStream is) throws IOException {
byte[] buffer = new byte[1024];
is.read(buffer);
// PE文件头检查(Windows可执行文件)
if (buffer.length > 60 && buffer[0] == 0x4D && buffer[1] == 0x5A) {
throw new IllegalArgumentException("检测到潜在有害文件内容");
}
// ELF文件头检查(Linux可执行文件)
if (buffer.length > 4 && buffer[0] == 0x7F && buffer[1] == 0x45 &&
buffer[2] == 0x4C && buffer[3] == 0x46) {
throw new IllegalArgumentException("检测到潜在有害文件内容");
}
}
}