-
四重驗證機制:
- 文件擴展名檢查(.xlsx/.xls)
- MIME類型檢查
- 文件魔數驗證(真實文件類型)
- 可執行文件特征檢測
-
防御措施:
- 使用
try-with-resources
確保流關閉 - 限制文件大小防止DoS攻擊
- 使用Apache POI的
FileMagic
進行專業驗證
- 使用
-
生產環境建議:
Yaml
# application.yml配置 spring: servlet: multipart: max-file-size: 10MB max-request-size: 10MB
完整代碼:?
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("檢測到潛在有害文件內容");}}
}