请求解密修改

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.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.Part;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
@ -66,6 +67,8 @@ public class RequestCoverFilter implements Filter {
handleUrlParams(httpRequest, response, chain, integrality, encrypt);
} else if (contentType.contains(MediaType.APPLICATION_JSON_VALUE)) {
handleBodyRequest(httpRequest, response, chain, integrality, encrypt);
} else if (contentType.contains(MediaType.MULTIPART_FORM_DATA_VALUE)) {
handleMultipartRequest(httpRequest, response, chain, integrality, encrypt);
} else {
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;
}
if (contentType.contains(MULTIPART_FORM_DATA_VALUE)) {
return true;
return false;
}
return false;
}
@ -73,13 +73,13 @@ public class ParamSecureHandler implements AsyncHandlerInterceptor {
/**
* 校验参数是否合法
*/
/*if (!requestWrapper.isChecked()) {
if (!requestWrapper.isChecked()) {
log.error("输入值非法: queryString={}, body={}",
StringUtils.defaultString(requestWrapper.getQueryString(), "null"),
StringUtils.defaultString(requestWrapper.getReaderParam(), "null"));
returnJson(response, "输入值非法", 500);
return false;
}*/
}
// System.err.println(JSON.toJSONString(request.getParameterMap()));
/**

View File

@ -31,14 +31,81 @@ public class XssRequestWrapper extends HttpServletRequestWrapper {
private String streamParam;
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) {
super(request);
// 不对查询串做篡改保持原样防止影响参数绑定
queryString = request.getQueryString();
// 先读取请求体数据
try {
// 读取请求体内容
// 使用 getInputStream() 而不是 getReader() 来避免冲突
ServletInputStream inputStream = request.getInputStream();
StringBuilder stringBuilder = new StringBuilder();
BufferedReader reader = request.getReader();
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, getCharacterEncoding(request)));
char[] charBuffer = new char[1024];
int bytesRead;
while ((bytesRead = reader.read(charBuffer)) > 0) {
@ -48,12 +115,10 @@ public class XssRequestWrapper extends HttpServletRequestWrapper {
// 不变更请求体只做检测
streamParam = requestBody;
body = streamParam.getBytes(request.getCharacterEncoding() != null ?
request.getCharacterEncoding() : "UTF-8");
body = streamParam.getBytes(getCharacterEncoding(request));
// 检查安全性仅校验不篡改
String queryStr = request.getQueryString();
setChecked(xssCleanNew(requestBody) && (queryStr == null || xssCleanNew(queryStr)));
setChecked(xssCleanNew(requestBody) && (queryString == null || xssCleanNew(queryString)));
} catch (IOException e) {
log.error("读取请求体数据失败: {}", e.getLocalizedMessage(), e);
@ -61,9 +126,12 @@ public class XssRequestWrapper extends HttpServletRequestWrapper {
streamParam = "";
setChecked(false);
}
}
// 不对查询串做篡改保持原样防止影响参数绑定
queryString = request.getQueryString();
// 辅助方法获取字符编码
private String getCharacterEncoding(HttpServletRequest request) {
String encoding = request.getCharacterEncoding();
return encoding != null ? encoding : "UTF-8";
}
@Override
@ -154,38 +222,15 @@ public class XssRequestWrapper extends HttpServletRequestWrapper {
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) {
if (value == 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("");
value = Pattern.compile(regex3, Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL).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("");
// 使用预编译的模式
for (Pattern pattern : XSS_PATTERNS) {
value = pattern.matcher(value).replaceAll("");
}
return value;
}
@ -200,21 +245,8 @@ public class XssRequestWrapper extends HttpServletRequestWrapper {
return isJsonSafe(value);
}
List<Pattern> patterns = new ArrayList<>();
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) {
// 使用预编译的模式进行检查
for (Pattern pattern : XSS_PATTERNS) {
if (match(pattern, value)) {
return false; // 发现XSS攻击
}
@ -224,19 +256,28 @@ public class XssRequestWrapper extends HttpServletRequestWrapper {
// 检查字符串是否为JSON格式
private boolean isJsonString(String str) {
try {
// 尝试解析为JSON对象
new JSONObject(str);
return true;
} catch (JSONException e1) {
if (str == null || str.trim().isEmpty()) {
return false;
}
String trimmed = str.trim();
if ((trimmed.startsWith("{") && trimmed.endsWith("}")) ||
(trimmed.startsWith("[") && trimmed.endsWith("]"))) {
try {
// 尝试解析为JSON数组
new JSONArray(str);
// 尝试解析为JSON对象
new JSONObject(trimmed);
return true;
} catch (JSONException e2) {
return false;
} catch (JSONException e1) {
try {
// 尝试解析为JSON数组
new JSONArray(trimmed);
return true;
} catch (JSONException e2) {
return false;
}
}
}
return false;
}
// 检查JSON字符串中的值是否安全
@ -285,6 +326,7 @@ public class XssRequestWrapper extends HttpServletRequestWrapper {
return false;
}
}
// 其他类型数字布尔值等视为安全
}
return true;
}
@ -307,6 +349,7 @@ public class XssRequestWrapper extends HttpServletRequestWrapper {
return false;
}
}
// 其他类型数字布尔值等视为安全
}
return true;
}
@ -317,22 +360,7 @@ public class XssRequestWrapper extends HttpServletRequestWrapper {
return true;
}
List<Pattern> patterns = new ArrayList<>();
// 这里添加你的所有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) {
for (Pattern pattern : XSS_PATTERNS) {
if (match(pattern, value)) {
return false;
}
@ -359,7 +387,6 @@ public class XssRequestWrapper extends HttpServletRequestWrapper {
this.checked = checked;
}
// 添加字符编码支持
public String getCharacterEncoding() {
String encoding = super.getCharacterEncoding();