From deb3594b40146777b749cc3b420ed632ae13959c Mon Sep 17 00:00:00 2001 From: RuoYi <yzz_ivy@163.com> Date: 星期二, 17 十一月 2020 10:21:19 +0800 Subject: [PATCH] 阻止任意文件下载漏洞 --- src/main/java/com/ruoyi/common/utils/file/FileUtils.java | 65 +++++++++++++++++++++ src/main/java/com/ruoyi/common/utils/file/FileTypeUtils.java | 47 +++++++++++++++ src/main/java/com/ruoyi/project/common/CommonController.java | 43 ++++++++----- 3 files changed, 136 insertions(+), 19 deletions(-) diff --git a/src/main/java/com/ruoyi/common/utils/file/FileTypeUtils.java b/src/main/java/com/ruoyi/common/utils/file/FileTypeUtils.java new file mode 100644 index 0000000..65be65b --- /dev/null +++ b/src/main/java/com/ruoyi/common/utils/file/FileTypeUtils.java @@ -0,0 +1,47 @@ +package com.ruoyi.common.utils.file; + +import java.io.File; +import org.apache.commons.lang3.StringUtils; + +/** + * 鏂囦欢绫诲瀷宸ュ叿绫� + * + * @author ruoyi + */ +public class FileTypeUtils +{ + /** + * 鑾峰彇鏂囦欢绫诲瀷 + * <p> + * 渚嬪: ruoyi.txt, 杩斿洖: txt + * + * @param file 鏂囦欢鍚� + * @return 鍚庣紑锛堜笉鍚�".") + */ + public static String getFileType(File file) + { + if (null == file) + { + return StringUtils.EMPTY; + } + return getFileType(file.getName()); + } + + /** + * 鑾峰彇鏂囦欢绫诲瀷 + * <p> + * 渚嬪: ruoyi.txt, 杩斿洖: txt + * + * @param fileName 鏂囦欢鍚� + * @return 鍚庣紑锛堜笉鍚�".") + */ + public static String getFileType(String fileName) + { + int separatorIndex = fileName.lastIndexOf("."); + if (separatorIndex < 0) + { + return ""; + } + return fileName.substring(separatorIndex + 1).toLowerCase(); + } +} \ No newline at end of file diff --git a/src/main/java/com/ruoyi/common/utils/file/FileUtils.java b/src/main/java/com/ruoyi/common/utils/file/FileUtils.java index f0dee60..6bc2ce6 100644 --- a/src/main/java/com/ruoyi/common/utils/file/FileUtils.java +++ b/src/main/java/com/ruoyi/common/utils/file/FileUtils.java @@ -7,7 +7,11 @@ import java.io.OutputStream; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import org.apache.commons.lang3.ArrayUtils; +import com.ruoyi.common.utils.StringUtils; /** * 鏂囦欢澶勭悊宸ュ叿绫� @@ -105,14 +109,37 @@ } /** + * 妫�鏌ユ枃浠舵槸鍚﹀彲涓嬭浇 + * + * @param resource 闇�瑕佷笅杞界殑鏂囦欢 + * @return true 姝e父 false 闈炴硶 + */ + public static boolean checkAllowDownload(String resource) + { + // 绂佹鐩綍涓婅烦绾у埆 + if (StringUtils.contains(resource, "..")) + { + return false; + } + + // 妫�鏌ュ厑璁镐笅杞界殑鏂囦欢瑙勫垯 + if (ArrayUtils.contains(MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION, FileTypeUtils.getFileType(resource))) + { + return true; + } + + // 涓嶅湪鍏佽涓嬭浇鐨勬枃浠惰鍒� + return false; + } + + /** * 涓嬭浇鏂囦欢鍚嶉噸鏂扮紪鐮� * * @param request 璇锋眰瀵硅薄 * @param fileName 鏂囦欢鍚� * @return 缂栫爜鍚庣殑鏂囦欢鍚� */ - public static String setFileDownloadHeader(HttpServletRequest request, String fileName) - throws UnsupportedEncodingException + public static String setFileDownloadHeader(HttpServletRequest request, String fileName) throws UnsupportedEncodingException { final String agent = request.getHeader("USER-AGENT"); String filename = fileName; @@ -139,4 +166,38 @@ } return filename; } + + /** + * 涓嬭浇鏂囦欢鍚嶉噸鏂扮紪鐮� + * + * @param response 鍝嶅簲瀵硅薄 + * @param realFileName 鐪熷疄鏂囦欢鍚� + * @return + */ + public static void setAttachmentResponseHeader(HttpServletResponse response, String realFileName) throws UnsupportedEncodingException + { + String percentEncodedFileName = percentEncode(realFileName); + + StringBuilder contentDispositionValue = new StringBuilder(); + contentDispositionValue.append("attachment; filename=") + .append(percentEncodedFileName) + .append(";") + .append("filename*=") + .append("utf-8''") + .append(percentEncodedFileName); + + response.setHeader("Content-disposition", contentDispositionValue.toString()); + } + + /** + * 鐧惧垎鍙风紪鐮佸伐鍏锋柟娉� + * + * @param s 闇�瑕佺櫨鍒嗗彿缂栫爜鐨勫瓧绗︿覆 + * @return 鐧惧垎鍙风紪鐮佸悗鐨勫瓧绗︿覆 + */ + public static String percentEncode(String s) throws UnsupportedEncodingException + { + String encode = URLEncoder.encode(s, StandardCharsets.UTF_8.toString()); + return encode.replaceAll("\\+", "%20"); + } } diff --git a/src/main/java/com/ruoyi/project/common/CommonController.java b/src/main/java/com/ruoyi/project/common/CommonController.java index b59fb29..4a73496 100644 --- a/src/main/java/com/ruoyi/project/common/CommonController.java +++ b/src/main/java/com/ruoyi/project/common/CommonController.java @@ -5,6 +5,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RestController; @@ -41,17 +42,15 @@ { try { - if (!FileUtils.isValidFilename(fileName)) + if (!FileUtils.checkAllowDownload(fileName)) { throw new Exception(StringUtils.format("鏂囦欢鍚嶇О({})闈炴硶锛屼笉鍏佽涓嬭浇銆� ", fileName)); } String realFileName = System.currentTimeMillis() + fileName.substring(fileName.indexOf("_") + 1); String filePath = RuoYiConfig.getDownloadPath() + fileName; - response.setCharacterEncoding("utf-8"); - response.setContentType("multipart/form-data"); - response.setHeader("Content-Disposition", - "attachment;fileName=" + FileUtils.setFileDownloadHeader(request, realFileName)); + response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE); + FileUtils.setAttachmentResponseHeader(response, realFileName); FileUtils.writeBytes(filePath, response.getOutputStream()); if (delete) { @@ -92,18 +91,28 @@ * 鏈湴璧勬簮閫氱敤涓嬭浇 */ @GetMapping("/common/download/resource") - public void resourceDownload(String name, HttpServletRequest request, HttpServletResponse response) throws Exception + public void resourceDownload(String resource, HttpServletRequest request, HttpServletResponse response) + throws Exception { - // 鏈湴璧勬簮璺緞 - String localPath = RuoYiConfig.getProfile(); - // 鏁版嵁搴撹祫婧愬湴鍧� - String downloadPath = localPath + StringUtils.substringAfter(name, Constants.RESOURCE_PREFIX); - // 涓嬭浇鍚嶇О - String downloadName = StringUtils.substringAfterLast(downloadPath, "/"); - response.setCharacterEncoding("utf-8"); - response.setContentType("multipart/form-data"); - response.setHeader("Content-Disposition", - "attachment;fileName=" + FileUtils.setFileDownloadHeader(request, downloadName)); - FileUtils.writeBytes(downloadPath, response.getOutputStream()); + try + { + if (!FileUtils.checkAllowDownload(resource)) + { + throw new Exception(StringUtils.format("璧勬簮鏂囦欢({})闈炴硶锛屼笉鍏佽涓嬭浇銆� ", resource)); + } + // 鏈湴璧勬簮璺緞 + String localPath = RuoYiConfig.getProfile(); + // 鏁版嵁搴撹祫婧愬湴鍧� + String downloadPath = localPath + StringUtils.substringAfter(resource, Constants.RESOURCE_PREFIX); + // 涓嬭浇鍚嶇О + String downloadName = StringUtils.substringAfterLast(downloadPath, "/"); + response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE); + FileUtils.setAttachmentResponseHeader(response, downloadName); + FileUtils.writeBytes(downloadPath, response.getOutputStream()); + } + catch (Exception e) + { + log.error("涓嬭浇鏂囦欢澶辫触", e); + } } } -- Gitblit v1.9.3