更新:文件类型检测白名单

This commit is contained in:
SillyBoy 2025-03-09 18:05:38 +08:00
parent bb68f050bf
commit 55fc58da3d
6 changed files with 174 additions and 15 deletions

View File

@ -57,6 +57,7 @@
<snakeyaml.version>2.2</snakeyaml.version> <snakeyaml.version>2.2</snakeyaml.version>
<freemarker.version>2.3.33</freemarker.version> <freemarker.version>2.3.33</freemarker.version>
<jsoup.version>1.18.1</jsoup.version> <jsoup.version>1.18.1</jsoup.version>
<tika.version>3.1.0</tika.version>
</properties> </properties>
<dependencyManagement> <dependencyManagement>
@ -325,6 +326,12 @@
<version>${freemarker.version}</version> <version>${freemarker.version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.apache.tika</groupId>
<artifactId>tika-core</artifactId>
<version>${tika.version}</version>
</dependency>
</dependencies> </dependencies>
</dependencyManagement> </dependencyManagement>

View File

@ -273,6 +273,11 @@
<artifactId>freemarker</artifactId> <artifactId>freemarker</artifactId>
</dependency> </dependency>
<dependency>
<groupId>org.apache.tika</groupId>
<artifactId>tika-core</artifactId>
</dependency>
</dependencies> </dependencies>

View File

@ -2,10 +2,19 @@ package net.lab1024.sa.base.module.support.securityprotect.service;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
import net.lab1024.sa.base.common.domain.ResponseDTO; import net.lab1024.sa.base.common.domain.ResponseDTO;
import org.apache.tika.config.TikaConfig;
import org.apache.tika.exception.TikaException;
import org.apache.tika.io.TikaInputStream;
import org.apache.tika.metadata.Metadata;
import org.apache.tika.metadata.TikaCoreProperties;
import org.apache.tika.mime.MediaType;
import org.apache.tika.mime.MimeTypes;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
import java.io.File; import java.io.IOException;
import java.util.Arrays;
import java.util.List;
/** /**
* 三级等保 文件上传 相关 * 三级等保 文件上传 相关
@ -23,6 +32,28 @@ public class SecurityFileService {
@Resource @Resource
private Level3ProtectConfigService level3ProtectConfigService; private Level3ProtectConfigService level3ProtectConfigService;
// 定义白名单MIME类型
private static final List<String> ALLOWED_MIME_TYPES = Arrays.asList(
"application/json",
"application/zip",
"application/x-7z-compressed",
"application/pdf",
"application/vnd.ms-excel",
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
"application/vnd.ms-powerpoint",
"application/vnd.openxmlformats-officedocument.presentationml.presentation",
"application/msword",
"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
"application/vnd.ms-works",
"text/csv",
"audio/*",
"video/*",
// 图片类型 svg有安全隐患所以不使用"image/*"
"image/jpeg",
"image/png",
"image/gif",
"image/bmp"
);
/** /**
* 检测文件安全类型 * 检测文件安全类型
@ -38,15 +69,50 @@ public class SecurityFileService {
} }
// 文件类型安全检测 // 文件类型安全检测
if (!level3ProtectConfigService.isFileDetectFlag()) { if (level3ProtectConfigService.isFileDetectFlag()) {
return ResponseDTO.ok(); String fileType = getFileMimeType(file);
if(ALLOWED_MIME_TYPES.stream()
.noneMatch(allowedType -> matchesMimeType(fileType, allowedType))){
return ResponseDTO.userErrorParam("禁止上传此文件类型");
}
} }
// 检测文件类型
// .....
return ResponseDTO.ok(); return ResponseDTO.ok();
} }
/**
* 获取文件的 MIME 类型
*
* @param file 要检查的文件
* @return 文件的 MIME 类型
*
*/
public static String getFileMimeType(MultipartFile file) {
try {
TikaConfig tika = new TikaConfig();
Metadata metadata = new Metadata();
metadata.set(TikaCoreProperties.RESOURCE_NAME_KEY, file.getOriginalFilename());
TikaInputStream stream = TikaInputStream.get(file.getInputStream());
MediaType mimetype = tika.getDetector().detect(stream, metadata);
return mimetype.toString();
} catch (IOException | TikaException e) {
return MimeTypes.OCTET_STREAM;
}
}
/**
* 检查文件的 MIME 类型是否与指定的MIME 类型匹配支持通配符
*
* @param fileType 文件的 MIME 类型
* @param mimetype MIME 类型支持通配符
* @return 是否匹配
*/
private static boolean matchesMimeType(String fileType, String mimetype) {
if (mimetype.endsWith("/*")) {
String prefix = mimetype.substring(0, mimetype.length() - 1);
return fileType.startsWith(prefix);
} else {
return fileType.equalsIgnoreCase(mimetype);
}
}
} }
;

View File

@ -59,6 +59,7 @@
<snakeyaml.version>2.2</snakeyaml.version> <snakeyaml.version>2.2</snakeyaml.version>
<freemarker.version>2.3.33</freemarker.version> <freemarker.version>2.3.33</freemarker.version>
<jsoup.version>1.18.1</jsoup.version> <jsoup.version>1.18.1</jsoup.version>
<tika.version>2.9.3</tika.version>
</properties> </properties>
<dependencyManagement> <dependencyManagement>
@ -370,6 +371,12 @@
<version>${freemarker.version}</version> <version>${freemarker.version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.apache.tika</groupId>
<artifactId>tika-core</artifactId>
<version>${tika.version}</version>
</dependency>
</dependencies> </dependencies>
</dependencyManagement> </dependencyManagement>

View File

@ -303,6 +303,11 @@
<artifactId>freemarker</artifactId> <artifactId>freemarker</artifactId>
</dependency> </dependency>
<dependency>
<groupId>org.apache.tika</groupId>
<artifactId>tika-core</artifactId>
<version>${tika.version}</version>
</dependency>
</dependencies> </dependencies>

View File

@ -1,11 +1,23 @@
package net.lab1024.sa.base.module.support.securityprotect.service; package net.lab1024.sa.base.module.support.securityprotect.service;
import net.lab1024.sa.base.common.domain.ResponseDTO; import net.lab1024.sa.base.common.domain.ResponseDTO;
import org.apache.tika.config.TikaConfig;
import org.apache.tika.detect.Detector;
import org.apache.tika.exception.TikaException;
import org.apache.tika.io.TikaInputStream;
import org.apache.tika.metadata.Metadata;
import org.apache.tika.metadata.TikaCoreProperties;
import org.apache.tika.mime.MediaType;
import org.apache.tika.mime.MimeTypes;
import org.apache.tika.parser.AutoDetectParser;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.io.File; import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
/** /**
* 三级等保 文件上传 相关 * 三级等保 文件上传 相关
@ -23,6 +35,28 @@ public class SecurityFileService {
@Resource @Resource
private Level3ProtectConfigService level3ProtectConfigService; private Level3ProtectConfigService level3ProtectConfigService;
// 定义白名单MIME类型
private static final List<String> ALLOWED_MIME_TYPES = Arrays.asList(
"application/json",
"application/zip",
"application/x-7z-compressed",
"application/pdf",
"application/vnd.ms-excel",
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
"application/vnd.ms-powerpoint",
"application/vnd.openxmlformats-officedocument.presentationml.presentation",
"application/msword",
"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
"application/vnd.ms-works",
"text/csv",
"audio/*",
"video/*",
// 图片类型 svg有安全隐患所以不使用"image/*"
"image/jpeg",
"image/png",
"image/gif",
"image/bmp"
);
/** /**
* 检测文件安全类型 * 检测文件安全类型
@ -38,15 +72,50 @@ public class SecurityFileService {
} }
// 文件类型安全检测 // 文件类型安全检测
if (!level3ProtectConfigService.isFileDetectFlag()) { if (level3ProtectConfigService.isFileDetectFlag()) {
return ResponseDTO.ok(); String fileType = getFileMimeType(file);
if(ALLOWED_MIME_TYPES.stream()
.noneMatch(allowedType -> matchesMimeType(fileType, allowedType))){
return ResponseDTO.userErrorParam("禁止上传此文件类型");
}
} }
// 检测文件类型
// .....
return ResponseDTO.ok(); return ResponseDTO.ok();
} }
/**
* 获取文件的 MIME 类型
*
* @param file 要检查的文件
* @return 文件的 MIME 类型
*
*/
public static String getFileMimeType(MultipartFile file) {
try {
TikaConfig tika = new TikaConfig();
Metadata metadata = new Metadata();
metadata.set(TikaCoreProperties.RESOURCE_NAME_KEY, file.getOriginalFilename());
TikaInputStream stream = TikaInputStream.get(file.getInputStream());
MediaType mimetype = tika.getDetector().detect(stream, metadata);
return mimetype.toString();
} catch (IOException | TikaException e) {
return MimeTypes.OCTET_STREAM;
}
}
/**
* 检查文件的 MIME 类型是否与指定的MIME 类型匹配支持通配符
*
* @param fileType 文件的 MIME 类型
* @param mimetype MIME 类型支持通配符
* @return 是否匹配
*/
private static boolean matchesMimeType(String fileType, String mimetype) {
if (mimetype.endsWith("/*")) {
String prefix = mimetype.substring(0, mimetype.length() - 1);
return fileType.startsWith(prefix);
} else {
return fileType.equalsIgnoreCase(mimetype);
}
}
} }
;