From 83478dfffa790703afe52aa372c93fcb165744e4 Mon Sep 17 00:00:00 2001 From: cwchen <1048842385@qq.com> Date: Mon, 29 Sep 2025 17:04:05 +0800 Subject: [PATCH] =?UTF-8?q?=E9=9D=9E=E6=B3=95=E5=80=BC=E6=A0=A1=E9=AA=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../interceptor/ParamSecureHandler.java | 16 +- .../bonus/framework/interceptor/XssCheck.java | 283 ++++++++++++++++-- 2 files changed, 270 insertions(+), 29 deletions(-) diff --git a/bonus-framework/src/main/java/com/bonus/framework/interceptor/ParamSecureHandler.java b/bonus-framework/src/main/java/com/bonus/framework/interceptor/ParamSecureHandler.java index 8d60e22..e952aa4 100644 --- a/bonus-framework/src/main/java/com/bonus/framework/interceptor/ParamSecureHandler.java +++ b/bonus-framework/src/main/java/com/bonus/framework/interceptor/ParamSecureHandler.java @@ -68,9 +68,19 @@ public class ParamSecureHandler implements AsyncHandlerInterceptor { // 过滤文件上传功能-只对参数进行校验 if (isFileUpload(request)) { String params = request.getParameter("params"); - boolean flag = XssCheck.xssCleanNew(params); - if(!flag){ - returnJson(response, "输入值非法", 500,null); +// boolean flag = XssCheck.xssCleanNew(params); + XssCheck.XssCheckResult xssCheckResult = XssCheck.xssCheckWithResult(null, params, null, null); + if(!xssCheckResult.isSafe()){ + Map map = new HashMap<>(16); +// returnJson(response, "输入值非法", 500,null); + List illegalParameters = xssCheckResult.getIllegalParameters(); + if(CollectionUtils.isNotEmpty(illegalParameters)){ + log.error("非法参数{}",illegalParameters); + XssCheck.IllegalParameter illegalParameter = illegalParameters.get(0); + map.put("paramName",illegalParameter.getParamName().replace("null.","")); + map.put("originalValue",illegalParameter.getOriginalValue()); + } + returnJson(response, "输入值非法", 500,map); return false; } return true; diff --git a/bonus-framework/src/main/java/com/bonus/framework/interceptor/XssCheck.java b/bonus-framework/src/main/java/com/bonus/framework/interceptor/XssCheck.java index 3357dd5..4b25ae2 100644 --- a/bonus-framework/src/main/java/com/bonus/framework/interceptor/XssCheck.java +++ b/bonus-framework/src/main/java/com/bonus/framework/interceptor/XssCheck.java @@ -1,26 +1,36 @@ package com.bonus.framework.interceptor; +import lombok.Data; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; +import java.util.*; import java.util.regex.Pattern; /** - * @className:XxlCheck - * @author:cwchen - * @date:2025-09-17-16:33 - * @version:1.0 - * @description: + * @className: XssCheck + * @author: cwchen + * @date: 2025-09-17-16:33 + * @version: 1.0 + * @description: XSS安全检查工具类 */ public class XssCheck { // 预编译所有正则表达式模式以提高性能 public static final List XSS_PATTERNS = new ArrayList<>(); + // 攻击类型常量 + public static final String ATTACK_TYPE_SCRIPT_TAG = "SCRIPT_TAG"; + public static final String ATTACK_TYPE_JAVASCRIPT_URL = "JAVASCRIPT_URL"; + public static final String ATTACK_TYPE_EVENT_HANDLER = "EVENT_HANDLER"; + public static final String ATTACK_TYPE_ALERT = "ALERT"; + public static final String ATTACK_TYPE_SPECIAL_CHARS = "SPECIAL_CHARS"; + public static final String ATTACK_TYPE_URL_ENCODING = "URL_ENCODING"; + public static final String ATTACK_TYPE_HEX_ENCODING = "HEX_ENCODING"; + public static final String ATTACK_TYPE_DATA_PROTOCOL = "DATA_PROTOCOL"; + public static final String ATTACK_TYPE_DOM_ACCESS = "DOM_ACCESS"; + static { // 初始化所有XSS模式 XSS_PATTERNS.add(Pattern.compile("", Pattern.CASE_INSENSITIVE)); @@ -79,7 +89,187 @@ public class XssCheck { XSS_PATTERNS.add(Pattern.compile("cookie", Pattern.CASE_INSENSITIVE)); } - static boolean xssCleanNew(String value) { + /** + * 非法参数信息类 + */ + @Data + public static class IllegalParameter { + private String paramName; // 参数名 + private String originalValue; // 原始值 + private String cleanedValue; // 清理后的值(可能为null) + private String attackType; // 攻击类型 + private Date detectedTime; // 检测时间 + private String requestUrl; // 请求URL + private String clientIp; // 客户端IP + } + + /** + * XSS检查结果类 + */ + @Data + public static class XssCheckResult { + private boolean isSafe; // 是否安全 + private List illegalParameters; // 非法参数列表 + + public XssCheckResult(boolean isSafe) { + this.isSafe = isSafe; + this.illegalParameters = new ArrayList<>(); + } + + public XssCheckResult(boolean isSafe, List illegalParameters) { + this.isSafe = isSafe; + this.illegalParameters = illegalParameters != null ? illegalParameters : new ArrayList<>(); + } + + /** + * 添加非法参数 + */ + public void addIllegalParameter(IllegalParameter illegalParameter) { + if (illegalParameter != null) { + this.illegalParameters.add(illegalParameter); + } + } + + /** + * 检查是否有非法参数 + */ + public boolean hasIllegalParameters() { + return !illegalParameters.isEmpty(); + } + } + + /** + * 增强的XSS检查方法,返回检查结果(包含boolean和非法参数列表) + */ + public static XssCheckResult xssCheckWithResult(String paramName, String value, String requestUrl, String clientIp) { + if (value == null) { + return new XssCheckResult(true); + } + + List illegalParameters = new ArrayList<>(); + + // 首先检查是否为JSON格式 + if (isJsonString(value)) { + // 对JSON字符串进行特殊处理:检查值部分是否包含XSS + collectJsonIllegalParameters(paramName, value, requestUrl, clientIp, illegalParameters); + } else { + // 使用预编译的模式进行检查 + IllegalParameter illegalParam = checkPatternsWithDetail(paramName, value, requestUrl, clientIp); + if (illegalParam != null) { + illegalParameters.add(illegalParam); + } + } + + boolean isSafe = illegalParameters.isEmpty(); + return new XssCheckResult(isSafe, illegalParameters); + } + + /** + * 批量检查多个参数 + */ + public static XssCheckResult xssCheckBatch(Map params, String requestUrl, String clientIp) { + List illegalParameters = new ArrayList<>(); + + for (Map.Entry entry : params.entrySet()) { + String paramName = entry.getKey(); + String value = entry.getValue(); + + if (value != null) { + // 首先检查是否为JSON格式 + if (isJsonString(value)) { + collectJsonIllegalParameters(paramName, value, requestUrl, clientIp, illegalParameters); + } else { + IllegalParameter illegalParam = checkPatternsWithDetail(paramName, value, requestUrl, clientIp); + if (illegalParam != null) { + illegalParameters.add(illegalParam); + } + } + } + } + + boolean isSafe = illegalParameters.isEmpty(); + return new XssCheckResult(isSafe, illegalParameters); + } + + /** + * 收集JSON中的非法参数 + */ + private static void collectJsonIllegalParameters(String paramName, String jsonStr, String requestUrl, String clientIp, List illegalParameters) { + try { + // 处理JSON对象 + if (jsonStr.trim().startsWith("{")) { + JSONObject jsonObject = new JSONObject(jsonStr); + collectJsonObjectIllegalParameters(paramName, jsonObject, requestUrl, clientIp, illegalParameters); + } + // 处理JSON数组 + else if (jsonStr.trim().startsWith("[")) { + JSONArray jsonArray = new JSONArray(jsonStr); + collectJsonArrayIllegalParameters(paramName, jsonArray, requestUrl, clientIp, illegalParameters); + } + } catch (JSONException e) { + // 解析失败,回退到普通字符串检查 + IllegalParameter illegalParam = checkPatternsWithDetail(paramName, jsonStr, requestUrl, clientIp); + if (illegalParam != null) { + illegalParameters.add(illegalParam); + } + } + } + + /** + * 收集JSON对象中的非法参数 + */ + private static void collectJsonObjectIllegalParameters(String parentKey, JSONObject jsonObject, String requestUrl, String clientIp, List illegalParameters) throws JSONException { + Iterator keys = jsonObject.keys(); + while (keys.hasNext()) { + String key = keys.next(); + Object value = jsonObject.get(key); + + // 检查key的安全性 + IllegalParameter keyIllegalParam = checkPatternsWithDetail(parentKey + "." + key, key, requestUrl, clientIp); + if (keyIllegalParam != null) { + illegalParameters.add(keyIllegalParam); + } + + // 检查value的安全性 + if (value instanceof String) { + IllegalParameter valueIllegalParam = checkPatternsWithDetail(parentKey + "." + key, (String) value, requestUrl, clientIp); + if (valueIllegalParam != null) { + illegalParameters.add(valueIllegalParam); + } + } else if (value instanceof JSONObject) { + collectJsonObjectIllegalParameters(parentKey + "." + key, (JSONObject) value, requestUrl, clientIp, illegalParameters); + } else if (value instanceof JSONArray) { + collectJsonArrayIllegalParameters(parentKey + "." + key, (JSONArray) value, requestUrl, clientIp, illegalParameters); + } + // 其他类型(数字、布尔值等)视为安全 + } + } + + /** + * 收集JSON数组中的非法参数 + */ + private static void collectJsonArrayIllegalParameters(String parentKey, JSONArray jsonArray, String requestUrl, String clientIp, List illegalParameters) throws JSONException { + for (int i = 0; i < jsonArray.length(); i++) { + Object value = jsonArray.get(i); + + if (value instanceof String) { + IllegalParameter illegalParam = checkPatternsWithDetail(parentKey + "[" + i + "]", (String) value, requestUrl, clientIp); + if (illegalParam != null) { + illegalParameters.add(illegalParam); + } + } else if (value instanceof JSONObject) { + collectJsonObjectIllegalParameters(parentKey + "[" + i + "]", (JSONObject) value, requestUrl, clientIp, illegalParameters); + } else if (value instanceof JSONArray) { + collectJsonArrayIllegalParameters(parentKey + "[" + i + "]", (JSONArray) value, requestUrl, clientIp, illegalParameters); + } + // 其他类型(数字、布尔值等)视为安全 + } + } + + /** + * 基本的XSS检查方法(保持原有逻辑)- 只返回boolean + */ + public static boolean xssCleanNew(String value) { if (value == null) { return true; } @@ -99,7 +289,62 @@ public class XssCheck { return true; // 安全 } - // 检查字符串是否为JSON格式 + /** + * 带详细信息的模式检查 + */ + private static IllegalParameter checkPatternsWithDetail(String paramName, String value, String requestUrl, String clientIp) { + for (int i = 0; i < XSS_PATTERNS.size(); i++) { + Pattern pattern = XSS_PATTERNS.get(i); + if (match(pattern, value)) { + IllegalParameter illegalParam = new IllegalParameter(); + illegalParam.setParamName(paramName); + illegalParam.setOriginalValue(value); + illegalParam.setAttackType(getAttackTypeByPatternIndex(i)); + illegalParam.setRequestUrl(requestUrl); + illegalParam.setClientIp(clientIp); + // 尝试生成清理后的值 + illegalParam.setCleanedValue(cleanXssContent(value)); + return illegalParam; + } + } + return null; + } + + /** + * 根据模式索引获取攻击类型 + */ + private static String getAttackTypeByPatternIndex(int index) { + if (index <= 5) return ATTACK_TYPE_SCRIPT_TAG; + if (index <= 8) return ATTACK_TYPE_JAVASCRIPT_URL; + if (index <= 13) return ATTACK_TYPE_EVENT_HANDLER; + if (index == 14) return ATTACK_TYPE_ALERT; + if (index <= 19) return ATTACK_TYPE_SPECIAL_CHARS; + if (index <= 30) return ATTACK_TYPE_URL_ENCODING; + if (index <= 35) return ATTACK_TYPE_HEX_ENCODING; + if (index <= 41) return ATTACK_TYPE_DATA_PROTOCOL; + return ATTACK_TYPE_DOM_ACCESS; + } + + /** + * 清理XSS内容(基础实现) + */ + private static String cleanXssContent(String value) { + if (value == null) return null; + + String cleaned = value; + // 移除script标签 + cleaned = cleaned.replaceAll("(?i).*?", ""); + // 移除javascript:协议 + cleaned = cleaned.replaceAll("(?i)javascript:", ""); + // 移除事件处理器 + cleaned = cleaned.replaceAll("(?i)on\\w+\\s*=", ""); + // 移除危险的HTML标签 + cleaned = cleaned.replaceAll("(?i)", ""); + + return cleaned.length() < value.length() ? cleaned : null; + } + + // 以下原有方法保持不变... public static boolean isJsonString(String str) { if (str == null || str.trim().isEmpty()) { return false; @@ -109,12 +354,10 @@ public class XssCheck { if ((trimmed.startsWith("{") && trimmed.endsWith("}")) || (trimmed.startsWith("[") && trimmed.endsWith("]"))) { try { - // 尝试解析为JSON对象 new JSONObject(trimmed); return true; } catch (JSONException e1) { try { - // 尝试解析为JSON数组 new JSONArray(trimmed); return true; } catch (JSONException e2) { @@ -125,39 +368,31 @@ public class XssCheck { return false; } - // 检查JSON字符串中的值是否安全 public static boolean isJsonSafe(String jsonStr) { try { - // 处理JSON对象 if (jsonStr.trim().startsWith("{")) { JSONObject jsonObject = new JSONObject(jsonStr); return isJsonObjectSafe(jsonObject); - } - // 处理JSON数组 - else if (jsonStr.trim().startsWith("[")) { + } else if (jsonStr.trim().startsWith("[")) { JSONArray jsonArray = new JSONArray(jsonStr); return isJsonArraySafe(jsonArray); } return true; } catch (JSONException e) { - // 解析失败,回退到普通字符串检查 return xssCleanNewFallback(jsonStr); } } - // 递归检查JSON对象的安全性 public static boolean isJsonObjectSafe(JSONObject jsonObject) throws JSONException { Iterator keys = jsonObject.keys(); while (keys.hasNext()) { String key = keys.next(); Object value = jsonObject.get(key); - // 检查key的安全性 if (!xssCleanNewFallback(key)) { return false; } - // 检查value的安全性 if (value instanceof String) { if (!xssCleanNewFallback((String) value)) { return false; @@ -171,12 +406,10 @@ public class XssCheck { return false; } } - // 其他类型(数字、布尔值等)视为安全 } return true; } - // 递归检查JSON数组的安全性 public static boolean isJsonArraySafe(JSONArray jsonArray) throws JSONException { for (int i = 0; i < jsonArray.length(); i++) { Object value = jsonArray.get(i); @@ -194,12 +427,10 @@ public class XssCheck { return false; } } - // 其他类型(数字、布尔值等)视为安全 } return true; } - // 回退到原始的模式匹配(避免递归调用) public static boolean xssCleanNewFallback(String value) { if (value == null) { return true; @@ -219,4 +450,4 @@ public class XssCheck { public static boolean match(Pattern pattern, String str) { return pattern.matcher(str).find(); } -} +} \ No newline at end of file