Merge remote-tracking branch 'origin/master'
This commit is contained in:
commit
714d1a69ac
|
|
@ -22,13 +22,36 @@ public class SafeUtil {
|
||||||
* 安全脚本模式,用于检测脚本注入的正则表达式
|
* 安全脚本模式,用于检测脚本注入的正则表达式
|
||||||
* 由于平台中setfilter中使用多个参数时用到&符号,因此未包含&符号
|
* 由于平台中setfilter中使用多个参数时用到&符号,因此未包含&符号
|
||||||
*/
|
*/
|
||||||
public final static String SAFE_SCRIPT_PATTERN =
|
/*public final static String SAFE_SCRIPT_PATTERN =
|
||||||
"(\\||;|\\$|'|\\'|0x0d|0x0a|\\%27|\\%3B" +
|
"(\\||;|\\$|'|\\'|0x0d|0x0a|\\%27|\\%3B" +
|
||||||
"|<>|\\[\\]|\\(\\)|/|\"" +
|
"|<>|\\[\\]|\\(\\)|/|\"" +
|
||||||
"|script|alert|svg|confirm|prompt|onload" +
|
"|script|alert|svg|confirm|prompt|onload" +
|
||||||
"|%3c|%3e|%2b|@|!|img|src" +
|
"|%3c|%3e|%2b|@|!|img|src" +
|
||||||
"|%)";
|
"|%)";*/
|
||||||
|
// 危险字符和编码
|
||||||
|
public final static String DANGEROUS_CHARS =
|
||||||
|
"(\\||;|\\$|'|\\'|0x0d|0x0a|\\%27|\\%3B" +
|
||||||
|
"|<>|\\[\\]|\\(\\)|/|\"" +
|
||||||
|
"|%3c|%3e|%2b|@|!|%)";
|
||||||
|
|
||||||
|
// JavaScript危险函数(带括号)
|
||||||
|
public final static String DANGEROUS_FUNCTIONS =
|
||||||
|
"(script\\s*\\(|alert\\s*\\(|confirm\\s*\\(|prompt\\s*\\(|eval\\s*\\(" +
|
||||||
|
"|setTimeout\\s*\\(|setInterval\\s*\\()";
|
||||||
|
|
||||||
|
// 危险HTML事件
|
||||||
|
public final static String DANGEROUS_EVENTS =
|
||||||
|
"(onload\\s*=|onclick\\s*=|onerror\\s*=|onmouseover\\s*=" +
|
||||||
|
"|onfocus\\s*=|onblur\\s*=)";
|
||||||
|
|
||||||
|
// 危险标签
|
||||||
|
public final static String DANGEROUS_TAGS =
|
||||||
|
"(<script|</script>|<iframe|</iframe>|<object|</object>)";
|
||||||
|
|
||||||
|
// 完整的XSS模式
|
||||||
|
public final static String COMPLETE_XSS_PATTERN =
|
||||||
|
DANGEROUS_CHARS + "|" + DANGEROUS_FUNCTIONS + "|" +
|
||||||
|
DANGEROUS_EVENTS + "|" + DANGEROUS_TAGS;
|
||||||
/**
|
/**
|
||||||
* 检查特殊字符的正则表达式
|
* 检查特殊字符的正则表达式
|
||||||
* 用于判断字符串是否为数字、字母、下划线或汉字
|
* 用于判断字符串是否为数字、字母、下划线或汉字
|
||||||
|
|
@ -61,6 +84,9 @@ public class SafeUtil {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static final Pattern XSS_PATTERN =
|
||||||
|
Pattern.compile(SafeUtil.COMPLETE_XSS_PATTERN, Pattern.CASE_INSENSITIVE);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 验证字符串是否包含特殊脚本字符
|
* 验证字符串是否包含特殊脚本字符
|
||||||
*
|
*
|
||||||
|
|
@ -68,10 +94,11 @@ public class SafeUtil {
|
||||||
* @return 如果包含特殊脚本字符返回true,否则返回false
|
* @return 如果包含特殊脚本字符返回true,否则返回false
|
||||||
*/
|
*/
|
||||||
public static boolean checkScript(String mark) {
|
public static boolean checkScript(String mark) {
|
||||||
if (mark != null && !"".equals(mark)) {
|
/*if (mark != null && !"".equals(mark)) {
|
||||||
return match(SAFE_SCRIPT_PATTERN, mark.toLowerCase().trim());
|
return match(SAFE_SCRIPT_PATTERN, mark.toLowerCase().trim());
|
||||||
}
|
}*/
|
||||||
return false;
|
if (mark == null) return false;
|
||||||
|
return XSS_PATTERN.matcher(mark).find();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import com.alibaba.fastjson2.JSON;
|
||||||
import com.bonus.common.core.domain.AjaxResult;
|
import com.bonus.common.core.domain.AjaxResult;
|
||||||
import com.bonus.common.utils.SafeUtil;
|
import com.bonus.common.utils.SafeUtil;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.apache.commons.collections4.CollectionUtils;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
import org.springframework.web.servlet.AsyncHandlerInterceptor;
|
import org.springframework.web.servlet.AsyncHandlerInterceptor;
|
||||||
|
|
@ -69,7 +70,7 @@ public class ParamSecureHandler implements AsyncHandlerInterceptor {
|
||||||
String params = request.getParameter("params");
|
String params = request.getParameter("params");
|
||||||
boolean flag = XssCheck.xssCleanNew(params);
|
boolean flag = XssCheck.xssCleanNew(params);
|
||||||
if(!flag){
|
if(!flag){
|
||||||
returnJson(response, "输入值非法", 500);
|
returnJson(response, "输入值非法", 500,null);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -81,10 +82,20 @@ public class ParamSecureHandler implements AsyncHandlerInterceptor {
|
||||||
* 校验参数是否合法
|
* 校验参数是否合法
|
||||||
*/
|
*/
|
||||||
if (!requestWrapper.isChecked()) {
|
if (!requestWrapper.isChecked()) {
|
||||||
log.error("输入值非法: queryString={}, body={}",
|
/*log.error("输入值非法: queryString={}, body={}",
|
||||||
StringUtils.defaultString(requestWrapper.getQueryString(), "null"),
|
StringUtils.defaultString(requestWrapper.getQueryString(), "null"),
|
||||||
StringUtils.defaultString(requestWrapper.getReaderParam(), "null"));
|
StringUtils.defaultString(requestWrapper.getReaderParam(), "null"));*/
|
||||||
returnJson(response, "输入值非法", 500);
|
|
||||||
|
Map<String, Object> map = new HashMap<>(16);
|
||||||
|
List<XssRequestWrapper.IllegalParameter> illegalParameters = requestWrapper.getIllegalParameters();
|
||||||
|
if(CollectionUtils.isNotEmpty(illegalParameters)){
|
||||||
|
log.error("非法参数{}",illegalParameters);
|
||||||
|
XssRequestWrapper.IllegalParameter illegalParameter = illegalParameters.get(0);
|
||||||
|
map.put("paramName",illegalParameter.getParamName().replace("REQUEST_BODY.",""));
|
||||||
|
map.put("originalValue",illegalParameter.getOriginalValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
returnJson(response, "输入值非法", 500,map);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// System.err.println(JSON.toJSONString(request.getParameterMap()));
|
// System.err.println(JSON.toJSONString(request.getParameterMap()));
|
||||||
|
|
@ -95,7 +106,7 @@ public class ParamSecureHandler implements AsyncHandlerInterceptor {
|
||||||
Map<String, String[]> map = requestWrapper.getParameterMap();
|
Map<String, String[]> map = requestWrapper.getParameterMap();
|
||||||
boolean checkParameterMap = checkParameterMap(map, requestUrl);
|
boolean checkParameterMap = checkParameterMap(map, requestUrl);
|
||||||
if (!checkParameterMap) {
|
if (!checkParameterMap) {
|
||||||
returnJson(response, "输入值非法", 500);
|
returnJson(response, "输入值非法", 500,null);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -108,7 +119,7 @@ public class ParamSecureHandler implements AsyncHandlerInterceptor {
|
||||||
if (!requestUrl.contains(uplFile) && !requestUrl.contains(upImage) && !requestUrl.contains(path)) {
|
if (!requestUrl.contains(uplFile) && !requestUrl.contains(upImage) && !requestUrl.contains(path)) {
|
||||||
boolean checkReader = checkReader(readerParam, requestUrl);
|
boolean checkReader = checkReader(readerParam, requestUrl);
|
||||||
if (!checkReader) {
|
if (!checkReader) {
|
||||||
returnJson(response, "不安全参数", 500);
|
returnJson(response, "不安全参数", 500,null);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -116,12 +127,12 @@ public class ParamSecureHandler implements AsyncHandlerInterceptor {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private void returnJson(HttpServletResponse response, String msg, int code) {
|
private void returnJson(HttpServletResponse response, String msg, int code,Object data) {
|
||||||
response.reset();
|
response.reset();
|
||||||
PrintWriter writer = null;
|
PrintWriter writer = null;
|
||||||
response.setCharacterEncoding("UTF-8");
|
response.setCharacterEncoding("UTF-8");
|
||||||
response.setContentType("applicatiopn/json;charset=utf-8");
|
response.setContentType("applicatiopn/json;charset=utf-8");
|
||||||
AjaxResult a = AjaxResult.error(code, msg);
|
AjaxResult a = AjaxResult.error(msg,data);
|
||||||
String res = JSON.toJSONString(a);
|
String res = JSON.toJSONString(a);
|
||||||
try {
|
try {
|
||||||
writer = response.getWriter();
|
writer = response.getWriter();
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
package com.bonus.framework.interceptor;
|
package com.bonus.framework.interceptor;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.json.JSONArray;
|
import org.json.JSONArray;
|
||||||
import org.json.JSONException;
|
import org.json.JSONException;
|
||||||
|
|
@ -13,10 +14,7 @@ import java.io.BufferedReader;
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
import java.util.ArrayList;
|
import java.util.*;
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -31,6 +29,9 @@ public class XssRequestWrapper extends HttpServletRequestWrapper {
|
||||||
private String streamParam;
|
private String streamParam;
|
||||||
private boolean checked;
|
private boolean checked;
|
||||||
|
|
||||||
|
// 新增:记录非法参数和攻击内容
|
||||||
|
private List<IllegalParameter> illegalParameters = new ArrayList<>();
|
||||||
|
|
||||||
// 预编译所有正则表达式模式以提高性能
|
// 预编译所有正则表达式模式以提高性能
|
||||||
private static final List<Pattern> XSS_PATTERNS = new ArrayList<>();
|
private static final List<Pattern> XSS_PATTERNS = new ArrayList<>();
|
||||||
|
|
||||||
|
|
@ -114,7 +115,9 @@ public class XssRequestWrapper extends HttpServletRequestWrapper {
|
||||||
|
|
||||||
// 检查安全性(仅校验,不篡改)
|
// 检查安全性(仅校验,不篡改)
|
||||||
String queryStr = request.getQueryString();
|
String queryStr = request.getQueryString();
|
||||||
setChecked(xssCleanNew(requestBody) && (queryStr == null || xssCleanNew(queryStr)));
|
boolean isBodySafe = xssCleanNew("REQUEST_BODY", requestBody);
|
||||||
|
boolean isQuerySafe = queryStr == null || xssCleanNew("QUERY_STRING", queryStr);
|
||||||
|
setChecked(isBodySafe && isQuerySafe);
|
||||||
|
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
log.error("读取请求体数据失败: {}", e.getLocalizedMessage(), e);
|
log.error("读取请求体数据失败: {}", e.getLocalizedMessage(), e);
|
||||||
|
|
@ -124,7 +127,7 @@ public class XssRequestWrapper extends HttpServletRequestWrapper {
|
||||||
}
|
}
|
||||||
|
|
||||||
// 不对查询串做篡改,保持原样,防止影响参数绑定
|
// 不对查询串做篡改,保持原样,防止影响参数绑定
|
||||||
queryString = request.getQueryString();
|
queryString = super.getQueryString();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -173,7 +176,7 @@ public class XssRequestWrapper extends HttpServletRequestWrapper {
|
||||||
String[] values = entry.getValue();
|
String[] values = entry.getValue();
|
||||||
for (int i = 0; i < values.length; i++) {
|
for (int i = 0; i < values.length; i++) {
|
||||||
if (values[i] != null) {
|
if (values[i] != null) {
|
||||||
values[i] = xssClean(values[i]);
|
values[i] = xssClean(entry.getKey(), values[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -191,7 +194,7 @@ public class XssRequestWrapper extends HttpServletRequestWrapper {
|
||||||
String[] cleanedValues = new String[values.length];
|
String[] cleanedValues = new String[values.length];
|
||||||
for (int i = 0; i < values.length; i++) {
|
for (int i = 0; i < values.length; i++) {
|
||||||
if (values[i] != null) {
|
if (values[i] != null) {
|
||||||
cleanedValues[i] = xssClean(values[i]);
|
cleanedValues[i] = xssClean(paramString, values[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return cleanedValues;
|
return cleanedValues;
|
||||||
|
|
@ -203,7 +206,7 @@ public class XssRequestWrapper extends HttpServletRequestWrapper {
|
||||||
if (value == null) {
|
if (value == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return xssClean(value);
|
return xssClean(paramString, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -212,22 +215,29 @@ public class XssRequestWrapper extends HttpServletRequestWrapper {
|
||||||
if (value == null) {
|
if (value == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return xssClean(value);
|
return xssClean(paramString, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
private String xssClean(String value) {
|
private String xssClean(String paramName, String value) {
|
||||||
if (value == null) {
|
if (value == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String cleanedValue = value;
|
||||||
// 使用预编译的模式
|
// 使用预编译的模式
|
||||||
for (Pattern pattern : XSS_PATTERNS) {
|
for (Pattern pattern : XSS_PATTERNS) {
|
||||||
value = pattern.matcher(value).replaceAll("");
|
cleanedValue = pattern.matcher(cleanedValue).replaceAll("");
|
||||||
}
|
}
|
||||||
return value;
|
|
||||||
|
// 如果值被修改,记录非法参数
|
||||||
|
if (!cleanedValue.equals(value)) {
|
||||||
|
recordIllegalParameter(paramName, value, cleanedValue, "XSS_ATTACK");
|
||||||
|
}
|
||||||
|
|
||||||
|
return cleanedValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean xssCleanNew(String value) {
|
private boolean xssCleanNew(String paramName, String value) {
|
||||||
if (value == null) {
|
if (value == null) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
@ -235,13 +245,15 @@ public class XssRequestWrapper extends HttpServletRequestWrapper {
|
||||||
// 首先检查是否为JSON格式
|
// 首先检查是否为JSON格式
|
||||||
if (isJsonString(value)) {
|
if (isJsonString(value)) {
|
||||||
// 对JSON字符串进行特殊处理:检查值部分是否包含XSS
|
// 对JSON字符串进行特殊处理:检查值部分是否包含XSS
|
||||||
return isJsonSafe(value);
|
return isJsonSafe(paramName, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 使用预编译的模式进行检查
|
// 使用预编译的模式进行检查
|
||||||
for (Pattern pattern : XSS_PATTERNS) {
|
for (Pattern pattern : XSS_PATTERNS) {
|
||||||
if (match(pattern, value)) {
|
if (match(pattern, value)) {
|
||||||
return false; // 发现XSS攻击
|
// 发现XSS攻击,记录非法参数
|
||||||
|
recordIllegalParameter(paramName, value, null, "XSS_PATTERN_" + pattern.pattern());
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true; // 安全
|
return true; // 安全
|
||||||
|
|
@ -274,91 +286,101 @@ public class XssRequestWrapper extends HttpServletRequestWrapper {
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查JSON字符串中的值是否安全
|
// 检查JSON字符串中的值是否安全
|
||||||
private boolean isJsonSafe(String jsonStr) {
|
private boolean isJsonSafe(String paramName, String jsonStr) {
|
||||||
try {
|
try {
|
||||||
// 处理JSON对象
|
// 处理JSON对象
|
||||||
if (jsonStr.trim().startsWith("{")) {
|
if (jsonStr.trim().startsWith("{")) {
|
||||||
JSONObject jsonObject = new JSONObject(jsonStr);
|
JSONObject jsonObject = new JSONObject(jsonStr);
|
||||||
return isJsonObjectSafe(jsonObject);
|
return isJsonObjectSafe(paramName, jsonObject);
|
||||||
}
|
}
|
||||||
// 处理JSON数组
|
// 处理JSON数组
|
||||||
else if (jsonStr.trim().startsWith("[")) {
|
else if (jsonStr.trim().startsWith("[")) {
|
||||||
JSONArray jsonArray = new JSONArray(jsonStr);
|
JSONArray jsonArray = new JSONArray(jsonStr);
|
||||||
return isJsonArraySafe(jsonArray);
|
return isJsonArraySafe(paramName, jsonArray);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
} catch (JSONException e) {
|
} catch (JSONException e) {
|
||||||
// 解析失败,回退到普通字符串检查
|
// 解析失败,回退到普通字符串检查
|
||||||
return xssCleanNewFallback(jsonStr);
|
return xssCleanNewFallback(paramName, jsonStr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 递归检查JSON对象的安全性
|
// 递归检查JSON对象的安全性
|
||||||
private boolean isJsonObjectSafe(JSONObject jsonObject) throws JSONException {
|
private boolean isJsonObjectSafe(String parentKey, JSONObject jsonObject) throws JSONException {
|
||||||
Iterator<String> keys = jsonObject.keys();
|
Iterator<String> keys = jsonObject.keys();
|
||||||
|
boolean isSafe = true;
|
||||||
|
|
||||||
while (keys.hasNext()) {
|
while (keys.hasNext()) {
|
||||||
String key = keys.next();
|
String key = keys.next();
|
||||||
Object value = jsonObject.get(key);
|
Object value = jsonObject.get(key);
|
||||||
|
|
||||||
|
String fullKey = parentKey + "." + key;
|
||||||
|
|
||||||
// 检查key的安全性
|
// 检查key的安全性
|
||||||
if (!xssCleanNewFallback(key)) {
|
if (!xssCleanNewFallback(fullKey + "[KEY]", key)) {
|
||||||
return false;
|
isSafe = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查value的安全性
|
// 检查value的安全性
|
||||||
if (value instanceof String) {
|
if (value instanceof String) {
|
||||||
if (!xssCleanNewFallback((String) value)) {
|
if (!xssCleanNewFallback(fullKey, (String) value)) {
|
||||||
return false;
|
isSafe = false;
|
||||||
}
|
}
|
||||||
} else if (value instanceof JSONObject) {
|
} else if (value instanceof JSONObject) {
|
||||||
if (!isJsonObjectSafe((JSONObject) value)) {
|
if (!isJsonObjectSafe(fullKey, (JSONObject) value)) {
|
||||||
return false;
|
isSafe = false;
|
||||||
}
|
}
|
||||||
} else if (value instanceof JSONArray) {
|
} else if (value instanceof JSONArray) {
|
||||||
if (!isJsonArraySafe((JSONArray) value)) {
|
if (!isJsonArraySafe(fullKey, (JSONArray) value)) {
|
||||||
return false;
|
isSafe = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 其他类型(数字、布尔值等)视为安全
|
// 其他类型(数字、布尔值等)视为安全
|
||||||
}
|
}
|
||||||
return true;
|
return isSafe;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 递归检查JSON数组的安全性
|
// 递归检查JSON数组的安全性
|
||||||
private boolean isJsonArraySafe(JSONArray jsonArray) throws JSONException {
|
private boolean isJsonArraySafe(String parentKey, JSONArray jsonArray) throws JSONException {
|
||||||
|
boolean isSafe = true;
|
||||||
|
|
||||||
for (int i = 0; i < jsonArray.length(); i++) {
|
for (int i = 0; i < jsonArray.length(); i++) {
|
||||||
Object value = jsonArray.get(i);
|
Object value = jsonArray.get(i);
|
||||||
|
String fullKey = parentKey + "[" + i + "]";
|
||||||
|
|
||||||
if (value instanceof String) {
|
if (value instanceof String) {
|
||||||
if (!xssCleanNewFallback((String) value)) {
|
if (!xssCleanNewFallback(fullKey, (String) value)) {
|
||||||
return false;
|
isSafe = false;
|
||||||
}
|
}
|
||||||
} else if (value instanceof JSONObject) {
|
} else if (value instanceof JSONObject) {
|
||||||
if (!isJsonObjectSafe((JSONObject) value)) {
|
if (!isJsonObjectSafe(fullKey, (JSONObject) value)) {
|
||||||
return false;
|
isSafe = false;
|
||||||
}
|
}
|
||||||
} else if (value instanceof JSONArray) {
|
} else if (value instanceof JSONArray) {
|
||||||
if (!isJsonArraySafe((JSONArray) value)) {
|
if (!isJsonArraySafe(fullKey, (JSONArray) value)) {
|
||||||
return false;
|
isSafe = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 其他类型(数字、布尔值等)视为安全
|
// 其他类型(数字、布尔值等)视为安全
|
||||||
}
|
}
|
||||||
return true;
|
return isSafe;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 回退到原始的模式匹配(避免递归调用)
|
// 回退到原始的模式匹配(避免递归调用)
|
||||||
private boolean xssCleanNewFallback(String value) {
|
private boolean xssCleanNewFallback(String paramName, String value) {
|
||||||
if (value == null) {
|
if (value == null) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
boolean isSafe = true;
|
||||||
for (Pattern pattern : XSS_PATTERNS) {
|
for (Pattern pattern : XSS_PATTERNS) {
|
||||||
if (match(pattern, value)) {
|
if (match(pattern, value)) {
|
||||||
return false;
|
recordIllegalParameter(paramName, value, null, "XSS_PATTERN_" + pattern.pattern());
|
||||||
|
isSafe = false;
|
||||||
|
// 继续检查其他模式,记录所有匹配的攻击模式
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return isSafe;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -368,6 +390,46 @@ public class XssRequestWrapper extends HttpServletRequestWrapper {
|
||||||
return pattern.matcher(str).find();
|
return pattern.matcher(str).find();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 记录非法参数信息
|
||||||
|
*/
|
||||||
|
private void recordIllegalParameter(String paramName, String originalValue, String cleanedValue, String attackType) {
|
||||||
|
IllegalParameter illegalParam = new IllegalParameter();
|
||||||
|
illegalParam.setParamName(paramName);
|
||||||
|
illegalParam.setOriginalValue(originalValue);
|
||||||
|
illegalParam.setCleanedValue(cleanedValue);
|
||||||
|
illegalParam.setAttackType(attackType);
|
||||||
|
illegalParam.setDetectedTime(new Date());
|
||||||
|
illegalParam.setRequestUrl(super.getRequestURL().toString());
|
||||||
|
illegalParam.setClientIp(getClientIp());
|
||||||
|
|
||||||
|
illegalParameters.add(illegalParam);
|
||||||
|
|
||||||
|
// 记录警告日志
|
||||||
|
/*log.warn("检测到XSS攻击尝试 - 参数: {}, 攻击类型: {}, 原始值: {}, 客户端IP: {}",
|
||||||
|
paramName, attackType,
|
||||||
|
originalValue.length() > 100 ? originalValue.substring(0, 100) + "..." : originalValue,
|
||||||
|
illegalParam.getClientIp());*/
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取客户端IP
|
||||||
|
*/
|
||||||
|
private String getClientIp() {
|
||||||
|
HttpServletRequest request = (HttpServletRequest) super.getRequest();
|
||||||
|
String ip = request.getHeader("X-Forwarded-For");
|
||||||
|
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
|
||||||
|
ip = request.getHeader("Proxy-Client-IP");
|
||||||
|
}
|
||||||
|
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
|
||||||
|
ip = request.getHeader("WL-Proxy-Client-IP");
|
||||||
|
}
|
||||||
|
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
|
||||||
|
ip = request.getRemoteAddr();
|
||||||
|
}
|
||||||
|
return ip;
|
||||||
|
}
|
||||||
|
|
||||||
public String getReaderParam() {
|
public String getReaderParam() {
|
||||||
return streamParam;
|
return streamParam;
|
||||||
}
|
}
|
||||||
|
|
@ -380,9 +442,44 @@ public class XssRequestWrapper extends HttpServletRequestWrapper {
|
||||||
this.checked = checked;
|
this.checked = checked;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 新增:获取非法参数列表
|
||||||
|
public List<IllegalParameter> getIllegalParameters() {
|
||||||
|
return Collections.unmodifiableList(illegalParameters);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 新增:检查是否有非法参数
|
||||||
|
public boolean hasIllegalParameters() {
|
||||||
|
return !illegalParameters.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 新增:获取非法参数数量
|
||||||
|
public int getIllegalParameterCount() {
|
||||||
|
return illegalParameters.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 新增:清空非法参数记录(用于重置)
|
||||||
|
public void clearIllegalParameters() {
|
||||||
|
illegalParameters.clear();
|
||||||
|
}
|
||||||
|
|
||||||
// 添加字符编码支持
|
// 添加字符编码支持
|
||||||
public String getCharacterEncoding() {
|
public String getCharacterEncoding() {
|
||||||
String encoding = super.getCharacterEncoding();
|
String encoding = super.getCharacterEncoding();
|
||||||
return encoding != null ? encoding : "UTF-8";
|
return encoding != null ? encoding : "UTF-8";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 非法参数信息类
|
||||||
|
*/
|
||||||
|
@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
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Loading…
Reference in New Issue