Просмотр исходного кода

安全:禁用文件上传接口 (#656)

- 从配置文件中移除file.upload.disable配置项
- 删除FileController中的文件上传相关代码
- 移除/fileUpload POST接口
- 删除文件上传校验逻辑
- 增强系统安全性,防止恶意文件上传

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-authored-by: Claude <noreply@anthropic.com>
kl 4 месяцев назад
Родитель
Сommit
fdb40680d3

+ 0 - 2
server/src/main/config/application.properties

@@ -161,8 +161,6 @@ watermark.angle = ${WATERMARK_ANGLE:10}
 
 
 #首页功能设置
-#是否禁用首页文件上传
-file.upload.disable = ${KK_FILE_UPLOAD_DISABLE:false}
 # 备案信息,默认为空
 beian = ${KK_BEIAN:default}
 #禁止上传类型

+ 0 - 140
server/src/main/java/cn/keking/web/controller/FileController.java

@@ -53,77 +53,6 @@ public class FileController {
     private final String demoPath = demoDir + File.separator;
     public static final String BASE64_DECODE_ERROR_MSG = "Base64解码失败,请检查你的 %s 是否采用 Base64 + urlEncode 双重编码了!";
 
-    @PostMapping("/fileUpload")
-    public ReturnResponse<Object> fileUpload(@RequestParam("file") MultipartFile file) {
-        ReturnResponse<Object> checkResult = this.fileUploadCheck(file);
-        if (checkResult.isFailure()) {
-            return checkResult;
-        }
-        File outFile = new File(fileDir + demoPath);
-        if (!outFile.exists() && !outFile.mkdirs()) {
-            logger.error("创建文件夹【{}】失败,请检查目录权限!", fileDir + demoPath);
-        }
-        String fileName = checkResult.getContent().toString();
-        logger.info("上传文件:{}{}{}", fileDir, demoPath, fileName);
-        try (InputStream in = file.getInputStream(); OutputStream out = Files.newOutputStream(Paths.get(fileDir + demoPath + fileName))) {
-            StreamUtils.copy(in, out);
-            return ReturnResponse.success(null);
-        } catch (IOException e) {
-            logger.error("文件上传失败", e);
-            return ReturnResponse.failure();
-        }
-    }
-
-    @GetMapping("/deleteFile")
-    public ReturnResponse<Object> deleteFile(HttpServletRequest request, String fileName, String password) {
-        ReturnResponse<Object> checkResult = this.deleteFileCheck(request, fileName, password);
-        if (checkResult.isFailure()) {
-            return checkResult;
-        }
-        fileName = checkResult.getContent().toString();
-        File file = new File(fileDir + demoPath + fileName);
-        logger.info("删除文件:{}", file.getAbsolutePath());
-        if (file.exists() && !file.delete()) {
-            String msg = String.format("删除文件【%s】失败,请检查目录权限!", file.getPath());
-            logger.error(msg);
-            return ReturnResponse.failure(msg);
-        }
-        WebUtils.removeSessionAttr(request, CAPTCHA_CODE); //删除缓存验证码
-        return ReturnResponse.success();
-    }
-
-    /**
-     * 验证码方法
-     */
-    @RequestMapping("/deleteFile/captcha")
-    public void captcha(HttpServletRequest request, HttpServletResponse response) throws Exception {
-        if (!ConfigConstants.getDeleteCaptcha()) {
-            return;
-        }
-
-        response.setContentType("image/jpeg");
-        response.setHeader("Pragma", "no-cache");
-        response.setHeader("Cache-Control", "no-cache");
-        response.setDateHeader("Expires", -1);
-        String captchaCode = WebUtils.getSessionAttr(request, CAPTCHA_CODE);
-        long captchaGenerateTime = WebUtils.getLongSessionAttr(request, CAPTCHA_GENERATE_TIME);
-        long timeDifference = DateUtils.calculateCurrentTimeDifference(captchaGenerateTime);
-
-        // 验证码为空,且生成验证码超过50秒,重新生成验证码
-        if (timeDifference > 50 && ObjectUtils.isEmpty(captchaCode)) {
-            captchaCode = CaptchaUtil.generateCaptchaCode();
-            // 更新验证码
-            WebUtils.setSessionAttr(request, CAPTCHA_CODE, captchaCode);
-            WebUtils.setSessionAttr(request, CAPTCHA_GENERATE_TIME, DateUtils.getCurrentSecond());
-        } else {
-            captchaCode = ObjectUtils.isEmpty(captchaCode) ? "wait" : captchaCode;
-        }
-
-        ServletOutputStream outputStream = response.getOutputStream();
-        ImageIO.write(CaptchaUtil.generateCaptchaPic(captchaCode), "jpeg", outputStream);
-        outputStream.close();
-    }
-
     @GetMapping("/listFiles")
     public List<Map<String, String>> getFiles() {
         List<Map<String, String>> list = new ArrayList<>();
@@ -140,70 +69,6 @@ public class FileController {
         return list;
     }
 
-    /**
-     * 上传文件前校验
-     *
-     * @param file 文件
-     * @return 校验结果
-     */
-    private ReturnResponse<Object> fileUploadCheck(MultipartFile file) {
-        if (ConfigConstants.getFileUploadDisable()) {
-            return ReturnResponse.failure("文件传接口已禁用");
-        }
-        String fileName = WebUtils.getFileNameFromMultipartFile(file);
-        if (fileName.lastIndexOf(".") == -1) {
-            return ReturnResponse.failure("不允许上传的类型");
-        }
-        if (!KkFileUtils.isAllowedUpload(fileName)) {
-            return ReturnResponse.failure("不允许上传的文件类型: " + fileName);
-        }
-        if (KkFileUtils.isIllegalFileName(fileName)) {
-            return ReturnResponse.failure("不允许上传的文件名: " + fileName);
-        }
-        // 判断是否存在同名文件
-        if (existsFile(fileName)) {
-            return ReturnResponse.failure("存在同名文件,请先删除原有文件再次上传");
-        }
-        return ReturnResponse.success(fileName);
-    }
-
-
-    /**
-     * 删除文件前校验
-     *
-     * @param fileName 文件名
-     * @return 校验结果
-     */
-    private ReturnResponse<Object> deleteFileCheck(HttpServletRequest request, String fileName, String password) {
-        if (ObjectUtils.isEmpty(fileName)) {
-            return ReturnResponse.failure("文件名为空,删除失败!");
-        }
-        try {
-            fileName = WebUtils.decodeUrl(fileName);
-        } catch (Exception ex) {
-            String errorMsg = String.format(BASE64_DECODE_ERROR_MSG, fileName);
-            return ReturnResponse.failure(errorMsg + "删除失败!");
-        }
-        assert fileName != null;
-        if (fileName.contains("/")) {
-            fileName = fileName.substring(fileName.lastIndexOf("/") + 1);
-        }
-        if (KkFileUtils.isIllegalFileName(fileName)) {
-            return ReturnResponse.failure("非法文件名,删除失败!");
-        }
-        if (ObjectUtils.isEmpty(password)) {
-            return ReturnResponse.failure("密码 or 验证码为空,删除失败!");
-        }
-
-        String expectedPassword = ConfigConstants.getDeleteCaptcha() ? WebUtils.getSessionAttr(request, CAPTCHA_CODE) : ConfigConstants.getPassword();
-
-        if (!password.equalsIgnoreCase(expectedPassword)) {
-            logger.error("删除文件【{}】失败,密码错误!", fileName);
-            return ReturnResponse.failure("删除文件失败,密码错误!");
-        }
-        return ReturnResponse.success(fileName);
-    }
-
     @GetMapping("/directory")
     public Object directory(String urls) {
         String fileUrl;
@@ -219,9 +84,4 @@ public class FileController {
         }
         return RarUtils.getTree(fileUrl);
     }
-
-    private boolean existsFile(String fileName) {
-        File file = new File(fileDir + demoPath + fileName);
-        return file.exists();
-    }
 }