本文主要是介绍XSS 跨站脚本攻击预防(文件上传),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
XSS 跨站脚本攻击预防(文件上传)
注意:可以根据需求自定义,改造为拦截器、或者 AOP 等方式实现
import cn.hutool.extra.spring.SpringUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.web.servlet.MultipartProperties;
import org.springframework.web.multipart.MultipartFile;import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;/*** 文件上传-文件信息校验工具类* 作用:防止 xss 跨站脚本攻击*/
@Slf4j
public class FileUploadCheckUtils {/*** spring.servlet.multipart的bean对象*/private static final MultipartProperties multipartProperties = SpringUtil.getBean(MultipartProperties.class);/*** 以下变量参数可以自行补充*/// 文件类型常量public static final String JPEG = "jpeg";public static final String JPG = "jpg";public static final String PNG = "png";public static final String GIF = "gif";public static final String PDF = "pdf";public static final String ZIP = "zip";public static final String RAR = "rar";public static final String DOC = "doc";public static final String DOCX = "docx";public static final String XLS = "xls";public static final String XLSX = "xlsx";public static final String PPT = "ppt";public static final String PPTX = "pptx";// 魔数常量public static final String JPEG_MAGIC = "FFD8FF";public static final String JPG_MAGIC = "FFD8FF";public static final String PNG_MAGIC = "89504E47";public static final String GIF_MAGIC = "47494638";public static final String PDF_MAGIC = "25504446";public static final String ZIP_MAGIC = "504B0304";public static final String RAR_MAGIC = "52617221";public static final String DOC_MAGIC = "D0CF11E0";public static final String DOCX_MAGIC = "504B0304";public static final String XLS_MAGIC = "D0CF11E0";public static final String XLSX_MAGIC = "504B0304";public static final String PPT_MAGIC = "D0CF11E0";public static final String PPTX_MAGIC = "504B0304";// 允许的文件类型// key-value : 文件类型-文件魔数private static final Map<String, String> FILE_TYPE_MAGIC_NUMBERS = new HashMap<>();static {FILE_TYPE_MAGIC_NUMBERS.put(JPEG, JPEG_MAGIC);FILE_TYPE_MAGIC_NUMBERS.put(JPG, JPG_MAGIC);FILE_TYPE_MAGIC_NUMBERS.put(PNG, PNG_MAGIC);FILE_TYPE_MAGIC_NUMBERS.put(GIF, GIF_MAGIC);FILE_TYPE_MAGIC_NUMBERS.put(PDF, PDF_MAGIC);FILE_TYPE_MAGIC_NUMBERS.put(ZIP, ZIP_MAGIC);FILE_TYPE_MAGIC_NUMBERS.put(RAR, RAR_MAGIC);FILE_TYPE_MAGIC_NUMBERS.put(DOC, DOC_MAGIC);FILE_TYPE_MAGIC_NUMBERS.put(DOCX, DOCX_MAGIC);FILE_TYPE_MAGIC_NUMBERS.put(XLS, XLS_MAGIC);FILE_TYPE_MAGIC_NUMBERS.put(XLSX, XLSX_MAGIC);FILE_TYPE_MAGIC_NUMBERS.put(PPT, PPT_MAGIC);FILE_TYPE_MAGIC_NUMBERS.put(PPTX, PPTX_MAGIC);}/*** @param file 被校验文件* @param allowFileMaxSize 允许的文件大小,单位为字节* @return bool*/public static void isValidFile(MultipartFile file, Long allowFileMaxSize, String... fileTypes) {// 检查文件大小if (!isValidFileSize(file, allowFileMaxSize)) {throw new RuntimeException("上传文件大小超过限制: " + allowFileMaxSize);}// 检查文件类型String fileExt = getFileExtension(file);if (!isValidFileType(fileExt, fileTypes)) {log.error("暂不支持文件类型: {}", fileExt);throw new RuntimeException("暂不支持文件类型: " + fileExt);}// 额外的魔数校验if (!isValidFileMagic(file, fileTypes)) {throw new RuntimeException("文件内容和文件类型不匹配");}}/*** @param fileSuffix 文件后缀* @param fileTypes 支持的文件类型* @return*/private static boolean isValidFileType(String fileSuffix, String[] fileTypes) {boolean flag = false;if (fileTypes == null || fileTypes.length == 0) {flag = FILE_TYPE_MAGIC_NUMBERS.containsKey(fileSuffix.toLowerCase());} else {for (String fileType : fileTypes) {if (fileSuffix.equals(fileType)) {flag = true;}}}return flag;}/*** 校验文件大小** @param file 文件* @param allowFileMaxSize 允许的文件大小* @return*/private static boolean isValidFileSize(MultipartFile file, Long allowFileMaxSize) {if (allowFileMaxSize == null) {allowFileMaxSize = multipartProperties.getMaxFileSize().toBytes();}log.info("上传文件大小为: {}", file.getSize());return file.getSize() <= allowFileMaxSize;}/*** 文件魔数校验** @param file 文件* @param fileTypes 文件类型* @return*/private static boolean isValidFileMagic(MultipartFile file, String[] fileTypes) {byte[] bytes = new byte[4];try (InputStream fis = file.getInputStream()) {fis.read(bytes, 0, bytes.length);StringBuilder sb = new StringBuilder();for (byte b : bytes) {sb.append(String.format("%02X", b));}String magicNumber = sb.toString();log.info("上传文件的魔数为: {}", magicNumber);if (fileTypes == null || fileTypes.length == 0) {for (String magic : FILE_TYPE_MAGIC_NUMBERS.values()) {if (magicNumber.startsWith(magic)) {return true;}}} else {for (String fileType : fileTypes) {String magic = FILE_TYPE_MAGIC_NUMBERS.get(fileType);if (magicNumber.startsWith(magic)) {return true;}}}} catch (Exception e) {log.error("上传文件-流读取操作异常: {}", e.getMessage());return false;}return false;}/*** 返回文件后缀** @param file 文件* @return string*/private static String getFileExtension(MultipartFile file) {String suffix = "";String originalFilename = file.getOriginalFilename();if (originalFilename != null) {int lastIndex = originalFilename.lastIndexOf('.');if (lastIndex > 0) {suffix = originalFilename.substring(lastIndex + 1);}}log.info("上传的文件后缀为: {}", suffix);return suffix;}
}
这篇关于XSS 跨站脚本攻击预防(文件上传)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!