Merge remote-tracking branch 'origin/master'

This commit is contained in:
liang.chao 2025-09-30 16:23:34 +08:00
commit 0791df7b71
2 changed files with 83 additions and 25 deletions

View File

@ -68,11 +68,9 @@ public class ParamSecureHandler implements AsyncHandlerInterceptor {
// 过滤文件上传功能-只对参数进行校验
if (isFileUpload(request)) {
String params = request.getParameter("params");
// boolean flag = XssCheck.xssCleanNew(params);
XssCheck.XssCheckResult xssCheckResult = XssCheck.xssCheckWithResult(null, params, null, null);
if(!xssCheckResult.isSafe()){
Map<String, Object> map = new HashMap<>(16);
// returnJson(response, "输入值非法", 500,null);
List<XssCheck.IllegalParameter> illegalParameters = xssCheckResult.getIllegalParameters();
if(CollectionUtils.isNotEmpty(illegalParameters)){
log.error("非法参数{}",illegalParameters);
@ -87,19 +85,15 @@ public class ParamSecureHandler implements AsyncHandlerInterceptor {
}
XssRequestWrapper requestWrapper = new XssRequestWrapper(request);
// System.err.println(JSON.toJSONString(request.getParameterMap()));
/**
* 校验参数是否合法
*/
if (!requestWrapper.isChecked()) {
/*log.error("输入值非法: queryString={}, body={}",
StringUtils.defaultString(requestWrapper.getQueryString(), "null"),
StringUtils.defaultString(requestWrapper.getReaderParam(), "null"));*/
Map<String, Object> map = new HashMap<>(16);
List<XssRequestWrapper.IllegalParameter> illegalParameters = requestWrapper.getIllegalParameters();
if(CollectionUtils.isNotEmpty(illegalParameters)){
log.error("非法参数{}",illegalParameters);
// log.error("非法参数{}",illegalParameters);
XssRequestWrapper.IllegalParameter illegalParameter = illegalParameters.get(0);
map.put("paramName",illegalParameter.getParamName().replace("REQUEST_BODY.",""));
map.put("originalValue",illegalParameter.getOriginalValue());
@ -108,7 +102,6 @@ public class ParamSecureHandler implements AsyncHandlerInterceptor {
returnJson(response, "输入值非法", 500,map);
return false;
}
// System.err.println(JSON.toJSONString(request.getParameterMap()));
/**
* 获取所有跳转路径参数保留传入下个界面
@ -123,7 +116,7 @@ public class ParamSecureHandler implements AsyncHandlerInterceptor {
/**
* 检查数据流参数
*/
String readerParam = requestWrapper.getReaderParam();
/*String readerParam = requestWrapper.getReaderParam();
// 判断是否是文件上传是不对流参数进行验证
String uplFile = "/upload", upImage = "/uploadFiles",path="/";
if (!requestUrl.contains(uplFile) && !requestUrl.contains(upImage) && !requestUrl.contains(path)) {
@ -132,7 +125,7 @@ public class ParamSecureHandler implements AsyncHandlerInterceptor {
returnJson(response, "不安全参数", 500,null);
return false;
}
}
}*/
return true;
}

View File

@ -35,7 +35,23 @@ public class XssRequestWrapper extends HttpServletRequestWrapper {
// 预编译所有正则表达式模式以提高性能
private static final List<Pattern> XSS_PATTERNS = new ArrayList<>();
// 需要忽略特殊字符校验的URL路径
private static final Set<String> IGNORE_SPECIAL_CHARS_URLS = new HashSet<>();
// 需要忽略的特殊字符模式在修改密码时需要忽略的
private static final List<Pattern> IGNORE_SPECIAL_PATTERNS = new ArrayList<>();
static {
// 初始化忽略校验的URL路径
IGNORE_SPECIAL_CHARS_URLS.add("/smartArchives/system/user/resetPwd");
IGNORE_SPECIAL_CHARS_URLS.add("/smartArchives/system/user/profile/updatePwd");
// 可以根据需要添加更多修改密码的URL
// 初始化需要忽略的特殊字符模式
IGNORE_SPECIAL_PATTERNS.add(Pattern.compile("@.*", Pattern.CASE_INSENSITIVE)); // @符号
IGNORE_SPECIAL_PATTERNS.add(Pattern.compile("!.*", Pattern.CASE_INSENSITIVE)); // 感叹号
IGNORE_SPECIAL_PATTERNS.add(Pattern.compile("%", Pattern.CASE_INSENSITIVE)); // %
// 初始化所有XSS模式
XSS_PATTERNS.add(Pattern.compile("<script>(.*?)</script>", Pattern.CASE_INSENSITIVE));
XSS_PATTERNS.add(Pattern.compile("src[\r\n]*=[\r\n]*'(.*?)'", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL));
@ -73,10 +89,7 @@ public class XssRequestWrapper extends HttpServletRequestWrapper {
XSS_PATTERNS.add(Pattern.compile("%5b", Pattern.CASE_INSENSITIVE)); // [ 的URL编码
XSS_PATTERNS.add(Pattern.compile("%5d", Pattern.CASE_INSENSITIVE)); // ] 的URL编码
// 其他特殊字符
XSS_PATTERNS.add(Pattern.compile("@.*", Pattern.CASE_INSENSITIVE)); // @符号
XSS_PATTERNS.add(Pattern.compile("!.*", Pattern.CASE_INSENSITIVE)); // 感叹号
XSS_PATTERNS.add(Pattern.compile("%", Pattern.CASE_INSENSITIVE)); // %
// 注意@!% 这三个模式被移到 IGNORE_SPECIAL_PATTERNS 不在 XSS_PATTERNS
// 十六进制编码
XSS_PATTERNS.add(Pattern.compile("\\\\x3c", Pattern.CASE_INSENSITIVE)); // < 的十六进制
@ -271,11 +284,19 @@ public class XssRequestWrapper extends HttpServletRequestWrapper {
}
String cleanedValue = value;
// 使用预编译的模式
// 首先使用主要XSS模式进行清理
for (Pattern pattern : XSS_PATTERNS) {
cleanedValue = pattern.matcher(cleanedValue).replaceAll("");
}
// 如果不是修改密码请求才对特殊字符进行清理
if (!isPasswordUpdateRequest()) {
for (Pattern pattern : IGNORE_SPECIAL_PATTERNS) {
cleanedValue = pattern.matcher(cleanedValue).replaceAll("");
}
}
// 如果值被修改记录非法参数
if (!cleanedValue.equals(value)) {
recordIllegalParameter(paramName, value, cleanedValue, "XSS_ATTACK");
@ -295,15 +316,42 @@ public class XssRequestWrapper extends HttpServletRequestWrapper {
return isJsonSafe(paramName, value);
}
// 使用预编译的模式进行检查
boolean isSafe = true;
// 使用预编译的模式进行检查主要XSS模式
for (Pattern pattern : XSS_PATTERNS) {
if (match(pattern, value)) {
// 发现XSS攻击记录非法参数
recordIllegalParameter(paramName, value, null, "XSS_PATTERN_" + pattern.pattern());
return false;
isSafe = false;
}
}
return true; // 安全
// 如果不是修改密码请求才对特殊字符进行检查
if (!isPasswordUpdateRequest()) {
for (Pattern pattern : IGNORE_SPECIAL_PATTERNS) {
if (match(pattern, value)) {
recordIllegalParameter(paramName, value, null, "SPECIAL_CHAR_PATTERN_" + pattern.pattern());
isSafe = false;
}
}
}
return isSafe;
}
/**
* 判断是否为修改密码请求
*/
private boolean isPasswordUpdateRequest() {
String requestURI = ((HttpServletRequest) getRequest()).getRequestURI();
for (String url : IGNORE_SPECIAL_CHARS_URLS) {
if (requestURI.contains(url)) {
log.debug("检测到修改密码请求,忽略特殊字符校验: {}", requestURI);
return true;
}
}
return false;
}
// 检查字符串是否为JSON格式
@ -420,13 +468,25 @@ public class XssRequestWrapper extends HttpServletRequestWrapper {
}
boolean isSafe = true;
// 检查主要XSS模式
for (Pattern pattern : XSS_PATTERNS) {
if (match(pattern, value)) {
recordIllegalParameter(paramName, value, null, "XSS_PATTERN_" + pattern.pattern());
isSafe = false;
// 继续检查其他模式记录所有匹配的攻击模式
}
}
// 如果不是修改密码请求才对特殊字符进行检查
if (!isPasswordUpdateRequest()) {
for (Pattern pattern : IGNORE_SPECIAL_PATTERNS) {
if (match(pattern, value)) {
recordIllegalParameter(paramName, value, null, "SPECIAL_CHAR_PATTERN_" + pattern.pattern());
isSafe = false;
}
}
}
return isSafe;
}
@ -441,6 +501,11 @@ public class XssRequestWrapper extends HttpServletRequestWrapper {
* 记录非法参数信息
*/
private void recordIllegalParameter(String paramName, String originalValue, String cleanedValue, String attackType) {
// 如果是修改密码请求且攻击类型是特殊字符则不记录
if (isPasswordUpdateRequest() && attackType.startsWith("SPECIAL_CHAR_PATTERN_")) {
return; // 忽略记录特殊字符攻击
}
IllegalParameter illegalParam = new IllegalParameter();
illegalParam.setParamName(paramName);
illegalParam.setOriginalValue(originalValue);
@ -452,11 +517,12 @@ public class XssRequestWrapper extends HttpServletRequestWrapper {
illegalParameters.add(illegalParam);
// 记录警告日志
/*log.warn("检测到XSS攻击尝试 - 参数: {}, 攻击类型: {}, 原始值: {}, 客户端IP: {}",
paramName, attackType,
originalValue.length() > 100 ? originalValue.substring(0, 100) + "..." : originalValue,
illegalParam.getClientIp());*/
// 记录警告日志特殊字符攻击在修改密码时不记录
if (!(isPasswordUpdateRequest() && attackType.startsWith("SPECIAL_CHAR_PATTERN_"))) {
log.warn("检测到安全威胁 - 参数: {}, 攻击类型: {}, 原始值: {}",
paramName, attackType,
originalValue.length() > 100 ? originalValue.substring(0, 100) + "..." : originalValue);
}
}
/**
@ -527,6 +593,5 @@ public class XssRequestWrapper extends HttpServletRequestWrapper {
private Date detectedTime; // 检测时间
private String requestUrl; // 请求URL
private String clientIp; // 客户端IP
}
}