检查是否存在upload文件漏洞 (上传文件类型不能是jsp、php、js、html等)

本文介绍了一个用于检查上传文件是否包含危险类型的Java方法。该方法通过验证文件名后缀,如.jsp、.php、.js、.htm、.html,来防止潜在的XSS攻击和非法文件上传。
/**
 * 检查是否存在upload文件漏洞
 * (上传文件类型不能是jsp、php、js、html等)
 * 
 * @param fileName
 * @throws Exception
 */
public static void checkUploadFileXssDanger(String fileName) throws Exception {
	if(fileName == null || fileName.trim().equals("")){
		return;
	}
	String tmpFileName = fileName.trim().toLowerCase();
	if(tmpFileName.endsWith(".jsp") ||  
	   tmpFileName.endsWith(".php") ||  	
	   tmpFileName.endsWith(".js")  ||  
	   tmpFileName.endsWith(".htm") ||  
	   tmpFileName.endsWith(".html"))
	{
		throw new Exception("上传文件类型不合法!");
	}
}

 

在 Java Web 应用中,文件上传是一个常见的功能,但若处理当(如仅依赖前端检查文件类型或使用安全的后端验证方式),极易导致安全漏洞,例如: - **任意文件上传**:攻击者上传 `.jsp`、`.php` 等可执行脚本文件,可能导致服务器被控制。 - **MIME 类型欺骗**:通过修改 HTTP 请求头中的 `Content-Type` 绕过类型检查。 - **扩展名绕过**:如使用 `.jsp.`、`.php5`、`.phtml` 等非常规扩展名绕过过滤。 --- ### ✅ 安全的文件上传检查方案 以下是一个完整的 Java 示例(基于 Spring Boot),展示如何安全地检查上传文件的类型,防止恶意文件上传。 ```java import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; import org.springframework.http.ResponseEntity; import java.io.File; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.HashSet; import java.util.Set; import java.util.Objects; @RestController @RequestMapping("/upload") public class FileUploadController { // 白名单:只允许安全的文件类型 private static final Set<String> ALLOWED_EXTENSIONS = new HashSet<>(Set.of( "jpg", "jpeg", "png", "gif", "pdf", "doc", "docx" )); // 白名单对应的 MIME 类型(用于双重校验) private static final Set<String> ALLOWED_MIME_TYPES = new HashSet<>(Set.of( "image/jpeg", "image/jpg", "image/png", "image/gif", "application/pdf", "application/msword", "application/vnd.openxmlformats-officedocument.wordprocessingml.document" )); private final Path uploadDir = Paths.get("uploads"); @PostMapping("/safe") public ResponseEntity<String> uploadFile(@RequestParam("file") MultipartFile file) { try { // 1. 检查文件是否为空 if (file.isEmpty()) { return ResponseEntity.badRequest().body("文件不能为空"); } // 2. 获取原始文件名并提取扩展名 String originalFilename = Objects.requireNonNull(file.getOriginalFilename()); String extension = getFileExtension(originalFilename).toLowerCase(); // 3. 校验扩展名(白名单) if (!ALLOWED_EXTENSIONS.contains(extension)) { return ResponseEntity.badRequest().body("允许的文件类型: " + extension); } // 4. 校验 MIME 类型(防止伪造扩展名) String mimeType = Files.probeContentType(file.getResource().getFile().toPath()); if (mimeType == null || !ALLOWED_MIME_TYPES.contains(mimeType)) { return ResponseEntity.badRequest().body("MIME 类型合法或未识别: " + mimeType); } // 5. 防止路径遍历攻击(检查文件是否包含 ../ 等) if (originalFilename.contains("..")) { return ResponseEntity.badRequest().body("文件包含非法字符"); } // 6. 使用 UUID 重命名文件,避免覆盖和路径注入 String safeFileName = java.util.UUID.randomUUID() + "." + extension; Path targetPath = uploadDir.resolve(safeFileName); // 7. 确保上传目录存在 if (!Files.exists(uploadDir)) { Files.createDirectories(uploadDir); } // 8. 保存文件 Files.copy(file.getInputStream(), targetPath); return ResponseEntity.ok("文件上传成功: " + safeFileName); } catch (IOException e) { return ResponseEntity.status(500).body("文件上传失败: " + e.getMessage()); } } // 提取文件扩展名 private String getFileExtension(String filename) { int lastDotIndex = filename.lastIndexOf('.'); if (lastDotIndex > 0 && lastDotIndex < filename.length() - 1) { return filename.substring(lastDotIndex + 1); } return ""; } } ``` --- ### 🔍 代码解释: | 步骤 | 说明 | |------|------| | 1 | 检查文件,避免空指针异常。 | | 2 | 获取原始文件名并提取扩展名。注意:不能只信任前端传来的扩展名。 | | 3 | **白名单机制**:只允许预定义的安全扩展名,拒绝 `.jsp`, `.php`, `.exe` 等危险类型。 | | 4 | 使用 `Files.probeContentType()` 探测真实 MIME 类型(基于文件内容),防止伪造扩展名(如把 `.jsp` 改成 `.jpg`)。 | | 5 | 防止路径遍历攻击(如 `../../../malicious.jsp`)。 | | 6 | 使用 UUID 重命名文件,避免文件名冲突和目录遍历。 | | 7 | 创建上传目录(如果存在)。 | | 8 | 将文件流写入磁盘。 | --- ### 🛡️ 进阶安全建议: 1. **禁止上传目录执行权限**: - 在服务器上配置上传目录可执行脚本(如 Apache/Nginx 设置 `deny .jsp`)。 2. **使用反病毒扫描**(如 ClamAV)对上传文件进行扫描。 3. **限制文件大小**: ```properties # application.properties spring.servlet.multipart.max-file-size=10MB spring.servlet.multipart.max-request-size=10MB ``` 4. **存储到非 Web 根目录**,并通过控制器代理访问。 5. **日志记录上传行为**,便于审计。 --- ### ❌ 常见错误做法(应避免): - 仅靠前端 JS 判断文件类型(易被绕过)。 - 使用黑名单过滤(永远无法穷举所有危险类型)。 - 直接使用用户上传文件名保存(可能导致覆盖或路径注入)。 - 未检查 MIME 类型,仅看扩展名。 --- ### ✅ 总结 通过 **白名单 + 内容探测 MIME 类型 + 安全重命名 + 路径防护** 的组合策略,可以有效防止安全文件上传。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ZHOU_VIP

您的鼓励将是我创作最大的动力!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值