请求解密修改

This commit is contained in:
cwchen 2025-09-17 15:31:23 +08:00
parent 7b8c202055
commit a7547bf00f
3 changed files with 237 additions and 80 deletions

View File

@ -12,6 +12,7 @@ import org.springframework.util.StreamUtils;
import javax.servlet.*; import javax.servlet.*;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper; import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.Part;
import java.io.BufferedReader; import java.io.BufferedReader;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.IOException; import java.io.IOException;
@ -66,6 +67,8 @@ public class RequestCoverFilter implements Filter {
handleUrlParams(httpRequest, response, chain, integrality, encrypt); handleUrlParams(httpRequest, response, chain, integrality, encrypt);
} else if (contentType.contains(MediaType.APPLICATION_JSON_VALUE)) { } else if (contentType.contains(MediaType.APPLICATION_JSON_VALUE)) {
handleBodyRequest(httpRequest, response, chain, integrality, encrypt); handleBodyRequest(httpRequest, response, chain, integrality, encrypt);
} else if (contentType.contains(MediaType.MULTIPART_FORM_DATA_VALUE)) {
handleMultipartRequest(httpRequest, response, chain, integrality, encrypt);
} else { } else {
chain.doFilter(request, response); chain.doFilter(request, response);
} }
@ -76,6 +79,22 @@ public class RequestCoverFilter implements Filter {
// 清理逻辑 // 清理逻辑
} }
/**
* 处理 multipart/form-data 请求
*/
private void handleMultipartRequest(HttpServletRequest request, ServletResponse response,
FilterChain chain, boolean integrality, boolean encrypt)
throws IOException, ServletException {
try {
// 创建包装的请求对象处理 multipart 请求
MultipartRequestWrapper wrappedRequest = new MultipartRequestWrapper(request, integrality, encrypt);
chain.doFilter(wrappedRequest, response);
} catch (Exception e) {
log.error("处理 multipart 请求时发生错误: {}", e.getMessage(), e);
throw new ServletException("请求处理失败", e);
}
}
/** /**
* 处理请求体里的参数 * 处理请求体里的参数
*/ */
@ -400,5 +419,116 @@ public class RequestCoverFilter implements Filter {
} }
} }
} /**
* Multipart 请求包装类
* 只处理 params 字段的解密params JSON 字符串忽略文件字段
*/
private static class MultipartRequestWrapper extends HttpServletRequestWrapper {
private final Map<String, String[]> parameterMap;
private final boolean integrality;
private final boolean encrypt;
public MultipartRequestWrapper(HttpServletRequest request, boolean integrality, boolean encrypt) {
super(request);
this.integrality = integrality;
this.encrypt = encrypt;
// 复制原始参数映射
this.parameterMap = new HashMap<>(request.getParameterMap());
// 处理 params 参数
processParams();
}
/**
* 处理 params 参数JSON 字符串格式
*/
private void processParams() {
String[] paramsValues = parameterMap.get("params");
if (paramsValues != null && paramsValues.length > 0) {
String encryptedParams = paramsValues[0];
try {
String decryptedParams = encryptedParams;
// 解密参数
if (encrypt) {
decryptedParams = Sm4Utils.decrypt(decryptedParams);
}
// 校验数据完整性
if (integrality) {
String[] parts = decryptedParams.split("\\|");
if (parts.length != 2) {
log.error("解密后的参数格式不正确: {}", decryptedParams);
throw new CaptchaException("请求参数不正确");
}
integrityVerification(parts[1], parts[0]);
decryptedParams = parts[0]; // 只保留原始 JSON 字符串部分
}
if (!ObjectUtils.isEmpty(decryptedParams)) {
// 由于 params JSON 字符串我们将其作为原始参数保留
// 而不是尝试解析为键值对以保持 JSON 结构完整
parameterMap.put("params", new String[]{decryptedParams});
} else {
// 移除空的 params 参数
parameterMap.remove("params");
}
} catch (Exception e) {
log.error("处理 multipart params 参数时发生错误: {}", e.getMessage(), e);
throw new CaptchaException("请求参数不正确");
}
}
}
/**
* 数据完整性校验
*/
private void integrityVerification(String providedHmac, String data) {
if (providedHmac == null) {
log.error("完整性校验哈希值为空");
throw new CaptchaException("请求参数不正确");
}
String calculatedHash = Sm3Util.encrypt(data);
log.info("计算出的哈希值: {}", calculatedHash);
log.info("提供的哈希值: {}", providedHmac);
if (!calculatedHash.equals(providedHmac)) {
log.error("参数完整性校验失败");
throw new CaptchaException("请求参数不正确");
}
}
@Override
public String getParameter(String name) {
String[] values = parameterMap.get(name);
return (values != null && values.length > 0) ? values[0] : null;
}
@Override
public Map<String, String[]> getParameterMap() {
return parameterMap;
}
@Override
public Enumeration<String> getParameterNames() {
return Collections.enumeration(parameterMap.keySet());
}
@Override
public String[] getParameterValues(String name) {
return parameterMap.get(name);
}
// 重写文件相关方法确保文件上传功能正常工作
@Override
public Collection<Part> getParts() throws IOException, ServletException {
return ((HttpServletRequest) getRequest()).getParts();
}
@Override
public Part getPart(String name) throws IOException, ServletException {
return ((HttpServletRequest) getRequest()).getPart(name);
}
}
}

View File

@ -50,7 +50,7 @@ public class ParamSecureHandler implements AsyncHandlerInterceptor {
return false; return false;
} }
if (contentType.contains(MULTIPART_FORM_DATA_VALUE)) { if (contentType.contains(MULTIPART_FORM_DATA_VALUE)) {
return true; return false;
} }
return false; return false;
} }
@ -73,13 +73,13 @@ 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); returnJson(response, "输入值非法", 500);
return false; return false;
}*/ }
// System.err.println(JSON.toJSONString(request.getParameterMap())); // System.err.println(JSON.toJSONString(request.getParameterMap()));
/** /**

View File

@ -31,14 +31,81 @@ public class XssRequestWrapper extends HttpServletRequestWrapper {
private String streamParam; private String streamParam;
private boolean checked; private boolean checked;
// 预编译所有正则表达式模式以提高性能
private static final List<Pattern> XSS_PATTERNS = new ArrayList<>();
static {
// 初始化所有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));
XSS_PATTERNS.add(Pattern.compile("src[\r\n]*=[\r\n]*\"(.*?)\"", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL));
XSS_PATTERNS.add(Pattern.compile("</script>", Pattern.CASE_INSENSITIVE));
XSS_PATTERNS.add(Pattern.compile("<script(.*?)>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL));
XSS_PATTERNS.add(Pattern.compile("eval\\((.*?)\\)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL));
XSS_PATTERNS.add(Pattern.compile("e-xpression\\((.*?)\\)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL));
XSS_PATTERNS.add(Pattern.compile("javascript:", Pattern.CASE_INSENSITIVE));
XSS_PATTERNS.add(Pattern.compile("vbscript:", Pattern.CASE_INSENSITIVE));
XSS_PATTERNS.add(Pattern.compile("onload(.*?)=", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL));
// 添加更多常见XSS模式
XSS_PATTERNS.add(Pattern.compile("onerror(.*?)=", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL));
XSS_PATTERNS.add(Pattern.compile("onclick(.*?)=", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL));
XSS_PATTERNS.add(Pattern.compile("onmouseover(.*?)=", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL));
XSS_PATTERNS.add(Pattern.compile("alert\\((.*?)\\)", Pattern.CASE_INSENSITIVE));
// 添加特殊字符和编码模式
XSS_PATTERNS.add(Pattern.compile("<.*>", Pattern.CASE_INSENSITIVE)); // 尖括号
XSS_PATTERNS.add(Pattern.compile("\\[.*\\]", Pattern.CASE_INSENSITIVE)); // 方括号
XSS_PATTERNS.add(Pattern.compile("\\(.*\\)", Pattern.CASE_INSENSITIVE)); // 圆括号
XSS_PATTERNS.add(Pattern.compile("/.*/", Pattern.CASE_INSENSITIVE)); // 斜杠
XSS_PATTERNS.add(Pattern.compile("'.*'", Pattern.CASE_INSENSITIVE)); // 单引号
XSS_PATTERNS.add(Pattern.compile("\".*\"", Pattern.CASE_INSENSITIVE)); // 双引号
// URL编码模式
XSS_PATTERNS.add(Pattern.compile("%3c", Pattern.CASE_INSENSITIVE)); // < 的URL编码
XSS_PATTERNS.add(Pattern.compile("%3e", Pattern.CASE_INSENSITIVE)); // > 的URL编码
XSS_PATTERNS.add(Pattern.compile("%2f", Pattern.CASE_INSENSITIVE)); // / 的URL编码
XSS_PATTERNS.add(Pattern.compile("%27", Pattern.CASE_INSENSITIVE)); // ' 的URL编码
XSS_PATTERNS.add(Pattern.compile("%22", Pattern.CASE_INSENSITIVE)); // " 的URL编码
XSS_PATTERNS.add(Pattern.compile("%2b", Pattern.CASE_INSENSITIVE)); // + 的URL编码
XSS_PATTERNS.add(Pattern.compile("%3b", Pattern.CASE_INSENSITIVE)); // ; 的URL编码
XSS_PATTERNS.add(Pattern.compile("%28", Pattern.CASE_INSENSITIVE)); // ( 的URL编码
XSS_PATTERNS.add(Pattern.compile("%29", Pattern.CASE_INSENSITIVE)); // ) 的URL编码
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("\\\\x3c", Pattern.CASE_INSENSITIVE)); // < 的十六进制
XSS_PATTERNS.add(Pattern.compile("\\\\x3e", Pattern.CASE_INSENSITIVE)); // > 的十六进制
XSS_PATTERNS.add(Pattern.compile("\\\\x2f", Pattern.CASE_INSENSITIVE)); // / 的十六进制
XSS_PATTERNS.add(Pattern.compile("\\\\x27", Pattern.CASE_INSENSITIVE)); // ' 的十六进制
XSS_PATTERNS.add(Pattern.compile("\\\\x22", Pattern.CASE_INSENSITIVE)); // " 的十六进制
// 添加更多常见的XSS绕过尝试
XSS_PATTERNS.add(Pattern.compile("data:text/html", Pattern.CASE_INSENSITIVE));
XSS_PATTERNS.add(Pattern.compile("base64", Pattern.CASE_INSENSITIVE));
XSS_PATTERNS.add(Pattern.compile("document\\.", Pattern.CASE_INSENSITIVE));
XSS_PATTERNS.add(Pattern.compile("window\\.", Pattern.CASE_INSENSITIVE));
XSS_PATTERNS.add(Pattern.compile("location\\.", Pattern.CASE_INSENSITIVE));
XSS_PATTERNS.add(Pattern.compile("cookie", Pattern.CASE_INSENSITIVE));
}
public XssRequestWrapper(HttpServletRequest request) { public XssRequestWrapper(HttpServletRequest request) {
super(request); super(request);
// 不对查询串做篡改保持原样防止影响参数绑定
queryString = request.getQueryString();
// 先读取请求体数据 // 先读取请求体数据
try { try {
// 读取请求体内容 // 使用 getInputStream() 而不是 getReader() 来避免冲突
ServletInputStream inputStream = request.getInputStream();
StringBuilder stringBuilder = new StringBuilder(); StringBuilder stringBuilder = new StringBuilder();
BufferedReader reader = request.getReader(); BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, getCharacterEncoding(request)));
char[] charBuffer = new char[1024]; char[] charBuffer = new char[1024];
int bytesRead; int bytesRead;
while ((bytesRead = reader.read(charBuffer)) > 0) { while ((bytesRead = reader.read(charBuffer)) > 0) {
@ -48,12 +115,10 @@ public class XssRequestWrapper extends HttpServletRequestWrapper {
// 不变更请求体只做检测 // 不变更请求体只做检测
streamParam = requestBody; streamParam = requestBody;
body = streamParam.getBytes(request.getCharacterEncoding() != null ? body = streamParam.getBytes(getCharacterEncoding(request));
request.getCharacterEncoding() : "UTF-8");
// 检查安全性仅校验不篡改 // 检查安全性仅校验不篡改
String queryStr = request.getQueryString(); setChecked(xssCleanNew(requestBody) && (queryString == null || xssCleanNew(queryString)));
setChecked(xssCleanNew(requestBody) && (queryStr == null || xssCleanNew(queryStr)));
} catch (IOException e) { } catch (IOException e) {
log.error("读取请求体数据失败: {}", e.getLocalizedMessage(), e); log.error("读取请求体数据失败: {}", e.getLocalizedMessage(), e);
@ -61,9 +126,12 @@ public class XssRequestWrapper extends HttpServletRequestWrapper {
streamParam = ""; streamParam = "";
setChecked(false); setChecked(false);
} }
}
// 不对查询串做篡改保持原样防止影响参数绑定 // 辅助方法获取字符编码
queryString = request.getQueryString(); private String getCharacterEncoding(HttpServletRequest request) {
String encoding = request.getCharacterEncoding();
return encoding != null ? encoding : "UTF-8";
} }
@Override @Override
@ -154,38 +222,15 @@ public class XssRequestWrapper extends HttpServletRequestWrapper {
return xssClean(value); return xssClean(value);
} }
// 正则表达式模式
private static final String regex1 = "<script>(.*?)</script>";
private static final String regex2 = "src[\r\n]*=[\r\n]*'(.*?)'";
private static final String regex3 = "src[\r\n]*=[\r\n]*\"(.*?)\"";
private static final String regex4 = "</script>";
private static final String regex5 = "<script(.*?)>";
private static final String regex6 = "eval\\((.*?)\\)";
private static final String regex7 = "e-xpression\\((.*?)\\)";
private static final String regex8 = "javascript:";
private static final String regex9 = "vbscript:";
private static final String regex10 = "onload(.*?)=";
// 安全模式移除过度严格的字符级别清理避免破坏 JSON/参数结构
// public static final String SAFE_SCRIPT_PATTERN = "(\\||;|\\$|'|\\'|0x0d|0x0a|\\%27|\\%3B|_|.)";
private String xssClean(String value) { private String xssClean(String value) {
if (value == null) { if (value == null) {
return null; return null;
} }
value = Pattern.compile(regex1, Pattern.CASE_INSENSITIVE).matcher(value).replaceAll(""); // 使用预编译的模式
value = Pattern.compile(regex2, Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL).matcher(value).replaceAll(""); for (Pattern pattern : XSS_PATTERNS) {
value = Pattern.compile(regex3, Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL).matcher(value).replaceAll(""); value = pattern.matcher(value).replaceAll("");
value = Pattern.compile(regex4, Pattern.CASE_INSENSITIVE).matcher(value).replaceAll(""); }
value = Pattern.compile(regex5, Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL).matcher(value).replaceAll("");
value = Pattern.compile(regex6, Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL).matcher(value).replaceAll("");
value = Pattern.compile(regex7, Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL).matcher(value).replaceAll("");
value = Pattern.compile(regex8, Pattern.CASE_INSENSITIVE).matcher(value).replaceAll("");
value = Pattern.compile(regex9, Pattern.CASE_INSENSITIVE).matcher(value).replaceAll("");
value = Pattern.compile(regex10, Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL).matcher(value).replaceAll("");
// 移除对普通字符的全量替换防止破坏字段名/JSON结构
// value = Pattern.compile(SAFE_SCRIPT_PATTERN, Pattern.CASE_INSENSITIVE).matcher(value).replaceAll("");
return value; return value;
} }
@ -200,21 +245,8 @@ public class XssRequestWrapper extends HttpServletRequestWrapper {
return isJsonSafe(value); return isJsonSafe(value);
} }
List<Pattern> patterns = new ArrayList<>(); // 使用预编译的模式进行检查
patterns.add(Pattern.compile(regex1, Pattern.CASE_INSENSITIVE)); for (Pattern pattern : XSS_PATTERNS) {
patterns.add(Pattern.compile(regex2, Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL));
patterns.add(Pattern.compile(regex3, Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL));
patterns.add(Pattern.compile(regex4, Pattern.CASE_INSENSITIVE));
patterns.add(Pattern.compile(regex5, Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL));
patterns.add(Pattern.compile(regex6, Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL));
patterns.add(Pattern.compile(regex7, Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL));
patterns.add(Pattern.compile(regex8, Pattern.CASE_INSENSITIVE));
patterns.add(Pattern.compile(regex9, Pattern.CASE_INSENSITIVE));
patterns.add(Pattern.compile(regex10, Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL));
// 移除过度严格的 SAFE_SCRIPT_PATTERN 检查
// patterns.add(Pattern.compile(SAFE_SCRIPT_PATTERN, Pattern.CASE_INSENSITIVE));
for (Pattern pattern : patterns) {
if (match(pattern, value)) { if (match(pattern, value)) {
return false; // 发现XSS攻击 return false; // 发现XSS攻击
} }
@ -224,19 +256,28 @@ public class XssRequestWrapper extends HttpServletRequestWrapper {
// 检查字符串是否为JSON格式 // 检查字符串是否为JSON格式
private boolean isJsonString(String str) { private boolean isJsonString(String str) {
try { if (str == null || str.trim().isEmpty()) {
// 尝试解析为JSON对象 return false;
new JSONObject(str); }
return true;
} catch (JSONException e1) { String trimmed = str.trim();
if ((trimmed.startsWith("{") && trimmed.endsWith("}")) ||
(trimmed.startsWith("[") && trimmed.endsWith("]"))) {
try { try {
// 尝试解析为JSON数组 // 尝试解析为JSON对象
new JSONArray(str); new JSONObject(trimmed);
return true; return true;
} catch (JSONException e2) { } catch (JSONException e1) {
return false; try {
// 尝试解析为JSON数组
new JSONArray(trimmed);
return true;
} catch (JSONException e2) {
return false;
}
} }
} }
return false;
} }
// 检查JSON字符串中的值是否安全 // 检查JSON字符串中的值是否安全
@ -285,6 +326,7 @@ public class XssRequestWrapper extends HttpServletRequestWrapper {
return false; return false;
} }
} }
// 其他类型数字布尔值等视为安全
} }
return true; return true;
} }
@ -307,6 +349,7 @@ public class XssRequestWrapper extends HttpServletRequestWrapper {
return false; return false;
} }
} }
// 其他类型数字布尔值等视为安全
} }
return true; return true;
} }
@ -317,22 +360,7 @@ public class XssRequestWrapper extends HttpServletRequestWrapper {
return true; return true;
} }
List<Pattern> patterns = new ArrayList<>(); for (Pattern pattern : XSS_PATTERNS) {
// 这里添加你的所有XSS模式...
patterns.add(Pattern.compile(regex1, Pattern.CASE_INSENSITIVE));
patterns.add(Pattern.compile(regex2, Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL));
patterns.add(Pattern.compile(regex3, Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL));
patterns.add(Pattern.compile(regex4, Pattern.CASE_INSENSITIVE));
patterns.add(Pattern.compile(regex5, Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL));
patterns.add(Pattern.compile(regex6, Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL));
patterns.add(Pattern.compile(regex7, Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL));
patterns.add(Pattern.compile(regex8, Pattern.CASE_INSENSITIVE));
patterns.add(Pattern.compile(regex9, Pattern.CASE_INSENSITIVE));
patterns.add(Pattern.compile(regex10, Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL));
// 移除过度严格的 SAFE_SCRIPT_PATTERN 检查
// patterns.add(Pattern.compile(SAFE_SCRIPT_PATTERN, Pattern.CASE_INSENSITIVE));
for (Pattern pattern : patterns) {
if (match(pattern, value)) { if (match(pattern, value)) {
return false; return false;
} }
@ -359,7 +387,6 @@ public class XssRequestWrapper extends HttpServletRequestWrapper {
this.checked = checked; this.checked = checked;
} }
// 添加字符编码支持 // 添加字符编码支持
public String getCharacterEncoding() { public String getCharacterEncoding() {
String encoding = super.getCharacterEncoding(); String encoding = super.getCharacterEncoding();