XSS 跨站脚本攻击预防(文件上传)
package com.aspire.sslca.cms.manage.util;
import cn.hutool.extra.spring.SpringUtil;
import com.aspire.webbas.common.lang.exception.ConditionNoPassException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.autoconfigure.web.servlet.MultipartProperties;
import org.springframework.web.multipart.MultipartFile;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Pattern;
public class FileUploadCheckUtils {
private static final Logger log = LoggerFactory.getLogger(FileUploadCheckUtils.class);
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);
}
// 定义更加全面的XSS攻击模式
private static final Pattern[] XSS_PATTERNS = new Pattern[]{
// 匹配script标签
Pattern.compile("<script>(.*?)</script>", Pattern.CASE_INSENSITIVE),
Pattern.compile("<script(.*?)>(.*?)</script>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL),
Pattern.compile("</script>", Pattern.CASE_INSENSITIVE),
Pattern.compile("<script(.*?)>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL),
// 匹配img、iframe、embed、object标签中的恶意代码
Pattern.compile("<img(.*?)src[\r\n]*=[\r\n]*\\'(.*?)\\'(.*?)>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL),
Pattern.compile("<img(.*?)src[\r\n]*=[\r\n]*\\\"(.*?)\\\"(.*?)>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL),
Pattern.compile("<iframe(.*?)src[\r\n]*=[\r\n]*\\'(.*?)\\'(.*?)>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL),
Pattern.compile("<iframe(.*?)src[\r\n]*=[\r\n]*\\\"(.*?)\\\"(.*?)>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL),
Pattern.compile("<embed(.*?)src[\r\n]*=[\r\n]*\\'(.*?)\\'(.*?)>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL),
Pattern.compile("<embed(.*?)src[\r\n]*=[\r\n]*\\\"(.*?)\\\"(.*?)>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL),
Pattern.compile("<object(.*?)data[\r\n]*=[\r\n]*\\'(.*?)\\'(.*?)>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL),
Pattern.compile("<object(.*?)data[\r\n]*=[\r\n]*\\\"(.*?)\\\"(.*?)>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL),
// 匹配JavaScript事件处理程序
Pattern.compile("onload(.*?)=", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL),
Pattern.compile("onerror(.*?)=", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL),
Pattern.compile("onmouseover(.*?)=", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL),
Pattern.compile("onclick(.*?)=", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL),
Pattern.compile("onfocus(.*?)=", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL),
Pattern.compile("onblur(.*?)=", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL),
Pattern.compile("onchange(.*?)=", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL),
Pattern.compile("onsubmit(.*?)=", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL),
Pattern.compile("onreset(.*?)=", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL),
Pattern.compile("onselect(.*?)=", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL),
Pattern.compile("onunload(.*?)=", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL),
Pattern.compile("onkeydown(.*?)=", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL),
Pattern.compile("onkeyup(.*?)=", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL),
Pattern.compile("onkeypress(.*?)=", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL),
// 匹配其他危险的JavaScript代码
Pattern.compile("eval\\((.*?)\\)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL),
Pattern.compile("expression\\((.*?)\\)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL),
Pattern.compile("javascript:", Pattern.CASE_INSENSITIVE),
Pattern.compile("vbscript:", Pattern.CASE_INSENSITIVE),
Pattern.compile("data:text/html", Pattern.CASE_INSENSITIVE),
Pattern.compile("document.cookie", Pattern.CASE_INSENSITIVE),
Pattern.compile("document.write", Pattern.CASE_INSENSITIVE),
Pattern.compile("window.location", Pattern.CASE_INSENSITIVE),
Pattern.compile("window.open", Pattern.CASE_INSENSITIVE),
Pattern.compile("innerHTML", Pattern.CASE_INSENSITIVE),
Pattern.compile("alert\\((.*?)\\)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL),
Pattern.compile("prompt\\((.*?)\\)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL),
Pattern.compile("confirm\\((.*?)\\)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL),
// 匹配各种形式的javascript关键字
Pattern.compile("/javascript", Pattern.MULTILINE | Pattern.DOTALL),
Pattern.compile("/JS", Pattern.MULTILINE | Pattern.DOTALL),
Pattern.compile("/JavaScript", Pattern.MULTILINE | Pattern.DOTALL),
Pattern.compile("/jscript", Pattern.MULTILINE | Pattern.DOTALL),
Pattern.compile("/vbscript", Pattern.MULTILINE | Pattern.DOTALL),
Pattern.compile("/ecmascript", Pattern.MULTILINE | Pattern.DOTALL),
// 匹配 CSS 表达式
Pattern.compile("style=(.*?)/\\*<style>\\*/", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL),
Pattern.compile("style=(.*?)expression\\((.*?)\\)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL),
Pattern.compile("style=(.*?)behaviour\\((.*?)\\)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL),
Pattern.compile("style=(.*?)javascript:(.*?)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL),
// 匹配 HTML 属性中的 JavaScript
Pattern.compile("href[\r\n]*=[\r\n]*\\\"(javascript:(.*?))\\\"", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL),
Pattern.compile("href[\r\n]*=[\r\n]*\\'(javascript:(.*?))\\'", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL),
Pattern.compile("src[\r\n]*=[\r\n]*\\\"(javascript:(.*?))\\\"", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL),
Pattern.compile("src[\r\n]*=[\r\n]*\\'(javascript:(.*?))\\'", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL),
// 匹配形式如 <a οnlοad=evil() /> 的 XSS
Pattern.compile("<(.*?)on(load|error|mouseover|click|focus|blur|change|submit|reset|select|unload|keydown|keyup|keypress)=(.*?)>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL),
// 匹配 base64 数据 URI
Pattern.compile("data:text/html;base64,", Pattern.CASE_INSENSITIVE),
// 匹配可能的路径级 XSS
Pattern.compile("/[a-zA-Z0-9\\-_]*[jJ][aA][vV][aA][sS][cC][rR][iI][pP][tT]/"),
Pattern.compile("/[a-zA-Z0-9\\-_]*[vV][bB][sS][cC][rR][iI][pP][tT]/"),
Pattern.compile("/[a-zA-Z0-9\\-_]*[eE][cC][mM][aA][sS][cC][rR][iI][pP][tT]/"),
// 匹配 XSS 关键字在任何位置的情况
Pattern.compile("[aA][lL][eE][rR][tT]\\("),