系统配置
This commit is contained in:
parent
15b6bef566
commit
a7ed6ae5ab
|
|
@ -19,7 +19,7 @@ server:
|
|||
port: 8080
|
||||
servlet:
|
||||
# 应用的访问路径
|
||||
context-path: /
|
||||
context-path: /smartArchives
|
||||
tomcat:
|
||||
# tomcat的URI编码
|
||||
uri-encoding: UTF-8
|
||||
|
|
@ -34,7 +34,7 @@ server:
|
|||
# 日志配置
|
||||
logging:
|
||||
level:
|
||||
com.bonus: debug
|
||||
com.bonus: info
|
||||
org.springframework: warn
|
||||
|
||||
# 用户配置
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ public class RequestCoverFilter implements Filter {
|
|||
|
||||
// 处理不同类型的请求
|
||||
if (contentType == null) {
|
||||
log.info("请求头中无Content-Type信息,处理URL参数。");
|
||||
// log.info("请求头中无Content-Type信息,处理URL参数。");
|
||||
handleUrlParams(httpRequest, response, chain, integrality, encrypt);
|
||||
} else if (contentType.contains(MediaType.APPLICATION_JSON_VALUE)) {
|
||||
handleBodyRequest(httpRequest, response, chain, integrality, encrypt);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,175 @@
|
|||
package com.bonus.common.utils;
|
||||
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* 安全验证的工具类
|
||||
* 提供用于检测SQL注入、特殊字符、脚本注入等的工具方法
|
||||
* 作者: GeYazhong
|
||||
* 日期: 2021/11/23 10:54
|
||||
*/
|
||||
public class SafeUtil {
|
||||
|
||||
/**
|
||||
* 安全SQL模式,用于检测SQL注入的正则表达式
|
||||
* 包含常见的SQL注入关键字和注释符号
|
||||
*/
|
||||
public final static String SAFE_SQL_PATTERN = "(?:')|(?:--)|(/\\*(?:.|[\\n\\r])*?\\*/)|"
|
||||
+ "(\\b(select|update|delete|insert|trancate|char|into|substr|ascii|declare|exec|count|master|into|drop|execute)\\b)";
|
||||
|
||||
/**
|
||||
* 安全脚本模式,用于检测脚本注入的正则表达式
|
||||
* 由于平台中setfilter中使用多个参数时用到&符号,因此未包含&符号
|
||||
*/
|
||||
public final static String SAFE_SCRIPT_PATTERN =
|
||||
"(\\||;|\\$|'|\\'|0x0d|0x0a|\\%27|\\%3B" +
|
||||
"|<>|\\[\\]|\\(\\)|/|\"" +
|
||||
"|script|alert|svg|confirm|prompt|onload" +
|
||||
"|%3c|%3e|%2b|@|!|img|src" +
|
||||
"|%|_)";
|
||||
|
||||
/**
|
||||
* 检查特殊字符的正则表达式
|
||||
* 用于判断字符串是否为数字、字母、下划线或汉字
|
||||
*/
|
||||
public static final String CHECK_SPECIAL = "^[\u4E00-\u9FA5A-Za-z_][\u4E00-\u9FA5A-Za-z0-9_]{0,}$";
|
||||
|
||||
/**
|
||||
* 验证字符串是否包含SQL注入关键字
|
||||
*
|
||||
* @param mark 需要检查的字符串
|
||||
* @return 如果包含SQL注入关键字返回true,否则返回false
|
||||
*/
|
||||
public static boolean checkSafeSql(String mark) {
|
||||
if (mark != null && !"".equals(mark)) {
|
||||
return match(SAFE_SQL_PATTERN, mark.toLowerCase().trim());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证字符串是否包含特殊字符
|
||||
*
|
||||
* @param mark 需要检查的字符串
|
||||
* @return 如果包含特殊字符返回true,否则返回false
|
||||
*/
|
||||
public static boolean checkSpecial(String mark) {
|
||||
if (mark != null && !"".equals(mark)) {
|
||||
return match(SAFE_SQL_PATTERN, mark.toLowerCase().trim());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证字符串是否包含特殊脚本字符
|
||||
*
|
||||
* @param mark 需要检查的字符串
|
||||
* @return 如果包含特殊脚本字符返回true,否则返回false
|
||||
*/
|
||||
public static boolean checkScript(String mark) {
|
||||
if (mark != null && !"".equals(mark)) {
|
||||
return match(SAFE_SCRIPT_PATTERN, mark.toLowerCase().trim());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行正则表达式匹配
|
||||
*
|
||||
* @param pattern 正则表达式模式
|
||||
* @param str 需要验证的字符串
|
||||
* @return 如果匹配返回true,否则返回false
|
||||
*/
|
||||
private static boolean match(String pattern, String str) {
|
||||
Pattern p = Pattern.compile(pattern);
|
||||
Matcher m = p.matcher(str);
|
||||
return m.find();
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查字符串是否符合正则表达式
|
||||
*
|
||||
* @param regex 正则表达式
|
||||
* @param orginal 需要检查的字符串
|
||||
* @return 如果匹配返回true,否则返回false
|
||||
*/
|
||||
private static boolean isMatch(String regex, String orginal) {
|
||||
if (StringUtils.isEmpty(orginal)) {
|
||||
return false;
|
||||
}
|
||||
Pattern pattern = Pattern.compile(regex);
|
||||
Matcher isNum = pattern.matcher(orginal);
|
||||
return isNum.matches();
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查字符串是否为正整数
|
||||
*
|
||||
* @param orginal 需要检查的字符串
|
||||
* @return 如果是正整数返回true,否则返回false
|
||||
*/
|
||||
public static boolean isPositiveInteger(String orginal) {
|
||||
return isMatch("^\\+{0,1}[1-9]\\d*", orginal);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查字符串是否为负整数
|
||||
*
|
||||
* @param orginal 需要检查的字符串
|
||||
* @return 如果是负整数返回true,否则返回false
|
||||
*/
|
||||
public static boolean isNegativeInteger(String orginal) {
|
||||
return isMatch("^-[1-9]\\d*", orginal);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查字符串是否为整数
|
||||
*
|
||||
* @param orginal 需要检查的字符串
|
||||
* @return 如果是整数返回true,否则返回false
|
||||
*/
|
||||
public static boolean isWholeNumber(String orginal) {
|
||||
return isMatch("[+-]{0,1}0", orginal) || isPositiveInteger(orginal) || isNegativeInteger(orginal);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查字符串是否为正小数
|
||||
*
|
||||
* @param orginal 需要检查的字符串
|
||||
* @return 如果是正小数返回true,否则返回false
|
||||
*/
|
||||
public static boolean isPositiveDecimal(String orginal) {
|
||||
return isMatch("\\+{0,1}[0]\\.[1-9]*|\\+{0,1}[1-9]\\d*\\.\\d*", orginal);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查字符串是否为负小数
|
||||
*
|
||||
* @param orginal 需要检查的字符串
|
||||
* @return 如果是负小数返回true,否则返回false
|
||||
*/
|
||||
public static boolean isNegativeDecimal(String orginal) {
|
||||
return isMatch("^-[0]\\.[1-9]*|^-[1-9]\\d*\\.\\d*", orginal);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查字符串是否为小数
|
||||
*
|
||||
* @param orginal 需要检查的字符串
|
||||
* @return 如果是小数返回true,否则返回false
|
||||
*/
|
||||
public static boolean isDecimal(String orginal) {
|
||||
return isMatch("[-+]{0,1}\\d+\\.\\d*|[-+]{0,1}\\d*\\.\\d+", orginal);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查字符串是否为实数(整数或小数)
|
||||
*
|
||||
* @param orginal 需要检查的字符串
|
||||
* @return 如果是实数返回true,否则返回false
|
||||
*/
|
||||
public static boolean isRealNumber(String orginal) {
|
||||
return isWholeNumber(orginal) || isDecimal(orginal);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,8 @@
|
|||
package com.bonus.framework.config;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import com.bonus.framework.interceptor.ParamSecureHandler;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
|
@ -26,6 +28,12 @@ public class ResourcesConfig implements WebMvcConfigurer
|
|||
@Autowired
|
||||
private RepeatSubmitInterceptor repeatSubmitInterceptor;
|
||||
|
||||
@Autowired
|
||||
private ParamSecureHandler paramSecureHandler;
|
||||
|
||||
/** 不需要拦截地址 "/logout",*/
|
||||
public static final String[] EXCLUDEURLS = { "/login", "/refresh" };
|
||||
|
||||
@Override
|
||||
public void addResourceHandlers(ResourceHandlerRegistry registry)
|
||||
{
|
||||
|
|
@ -46,6 +54,11 @@ public class ResourcesConfig implements WebMvcConfigurer
|
|||
public void addInterceptors(InterceptorRegistry registry)
|
||||
{
|
||||
registry.addInterceptor(repeatSubmitInterceptor).addPathPatterns("/**");
|
||||
//自定义拦截器
|
||||
registry.addInterceptor(paramSecureHandler)
|
||||
.addPathPatterns("/**")
|
||||
.excludePathPatterns(EXCLUDEURLS)
|
||||
.order(-10);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -33,11 +33,11 @@ import org.springframework.stereotype.Component;
|
|||
public class ReplayAttackFilter implements Filter {
|
||||
static List<String> ignoreUrlPatterns = new ArrayList<>();
|
||||
static {
|
||||
ignoreUrlPatterns.add("/captchaImage");
|
||||
ignoreUrlPatterns.add("/login");
|
||||
ignoreUrlPatterns.add("/loginOut");
|
||||
ignoreUrlPatterns.add("/getInfo");
|
||||
ignoreUrlPatterns.add("/getRouters");
|
||||
ignoreUrlPatterns.add("/smartArchives/captchaImage");
|
||||
ignoreUrlPatterns.add("/smartArchives/login");
|
||||
ignoreUrlPatterns.add("/smartArchives/logout");
|
||||
ignoreUrlPatterns.add("/smartArchives/getInfo");
|
||||
ignoreUrlPatterns.add("/smartArchives/getRouters");
|
||||
}
|
||||
|
||||
@Autowired
|
||||
|
|
@ -69,7 +69,6 @@ public class ReplayAttackFilter implements Filter {
|
|||
boolean shouldIgnore = ignoreUrlPatterns.contains(requestURI);
|
||||
if (shouldIgnore) {
|
||||
// 忽略该请求的处理
|
||||
System.out.println("忽略请求: " + requestURI);
|
||||
chain.doFilter(request, response);
|
||||
return;
|
||||
}
|
||||
|
|
@ -172,7 +171,6 @@ public class ReplayAttackFilter implements Filter {
|
|||
|
||||
// 构建待签名字符串
|
||||
String signString = buildSignString(encryptUserId, timestamp, requestUrl, request.getMethod());
|
||||
System.err.println(signString);
|
||||
// 使用HMAC-SHA256计算签名
|
||||
String calculatedSignature = calculateHMAC(signString, encryptSecret);
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,241 @@
|
|||
package com.bonus.framework.interceptor;
|
||||
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.bonus.common.core.domain.AjaxResult;
|
||||
import com.bonus.common.utils.SafeUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.servlet.AsyncHandlerInterceptor;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.servlet.http.HttpSession;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.*;
|
||||
|
||||
import static org.springframework.http.MediaType.MULTIPART_FORM_DATA_VALUE;
|
||||
|
||||
/**
|
||||
* @author bonus
|
||||
* @data 2023/2/6 17:22
|
||||
* @description 安全参数验证
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class ParamSecureHandler implements AsyncHandlerInterceptor {
|
||||
|
||||
static List<String> ignoreUrlPatterns = new ArrayList<>();
|
||||
static {
|
||||
ignoreUrlPatterns.add("/smartArchives/captchaImage");
|
||||
ignoreUrlPatterns.add("/smartArchives/login");
|
||||
ignoreUrlPatterns.add("/smartArchives/logout");
|
||||
ignoreUrlPatterns.add("/smartArchives/getInfo");
|
||||
ignoreUrlPatterns.add("/smartArchives/getRouters");
|
||||
}
|
||||
private String rnd = null;
|
||||
|
||||
public static String ur = "/";
|
||||
|
||||
public static int max_length = 4;
|
||||
|
||||
private static Map<String, List<Double>> requestLogMap = null;
|
||||
|
||||
public boolean isFileUpload(HttpServletRequest request) {
|
||||
String contentType = request.getContentType();
|
||||
if (StringUtils.isEmpty(contentType)) {
|
||||
return false;
|
||||
}
|
||||
if (contentType.contains(MULTIPART_FORM_DATA_VALUE)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
|
||||
// 过滤部分请求
|
||||
String requestUrl = request.getRequestURI();
|
||||
boolean shouldIgnore = ignoreUrlPatterns.contains(requestUrl);
|
||||
if (shouldIgnore) {
|
||||
return true;
|
||||
}
|
||||
// 过滤文件上传功能
|
||||
if (isFileUpload(request)) {
|
||||
return true;
|
||||
}
|
||||
XssRequestWrapper requestWrapper = new XssRequestWrapper(request);
|
||||
|
||||
/**
|
||||
* 校验参数是否合法
|
||||
*/
|
||||
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()));
|
||||
|
||||
/**
|
||||
* 获取所有跳转路径参数,保留传入下个界面
|
||||
*/
|
||||
Map<String, String[]> map = requestWrapper.getParameterMap();
|
||||
boolean checkParameterMap = checkParameterMap(map, requestUrl);
|
||||
if (!checkParameterMap) {
|
||||
returnJson(response, "输入值非法", 500);
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查数据流参数
|
||||
*/
|
||||
String readerParam = requestWrapper.getReaderParam();
|
||||
// 判断是否是文件上传,是不对流参数进行验证
|
||||
String uplFile = "/upload", upImage = "/uploadFiles",path="/";
|
||||
if (!requestUrl.contains(uplFile) && !requestUrl.contains(upImage) && !requestUrl.contains(path)) {
|
||||
boolean checkReader = checkReader(readerParam, requestUrl);
|
||||
if (!checkReader) {
|
||||
returnJson(response, "不安全参数", 500);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
private void returnJson(HttpServletResponse response, String msg, int code) {
|
||||
response.reset();
|
||||
PrintWriter writer = null;
|
||||
response.setCharacterEncoding("UTF-8");
|
||||
response.setContentType("applicatiopn/json;charset=utf-8");
|
||||
AjaxResult a = AjaxResult.error(code, msg);
|
||||
String res = JSON.toJSONString(a);
|
||||
try {
|
||||
writer = response.getWriter();
|
||||
writer.println(res);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查所有页面输入的参数是否安全 request.getParameterMap()
|
||||
*
|
||||
* @param map
|
||||
* @param requestUrl
|
||||
*/
|
||||
private boolean checkParameterMap(Map<String, String[]> map, String requestUrl) {
|
||||
rnd = null;
|
||||
if (map != null && map.size() > 0) {
|
||||
Iterator<Map.Entry<String, String[]>> iterator = map.entrySet().iterator();
|
||||
String value = "";
|
||||
while (iterator.hasNext()) {
|
||||
value = "";
|
||||
Map.Entry<String, String[]> entry = iterator.next();
|
||||
String pname = (String) entry.getKey();
|
||||
if (Objects.equals("token", pname)) {
|
||||
continue;
|
||||
}
|
||||
Object objValue = entry.getValue();
|
||||
if (null == objValue) {
|
||||
value = "";
|
||||
} else {
|
||||
String[] values = (String[]) objValue;
|
||||
for (String s : values) {
|
||||
value = s + ",";
|
||||
}
|
||||
if (value.length() > 0) {
|
||||
value = value.substring(0, value.length() - 1);
|
||||
}
|
||||
}
|
||||
|
||||
if ("rnd".equals(pname)) {
|
||||
rnd = value;
|
||||
}
|
||||
if (SafeUtil.checkSafeSql(value)) {
|
||||
log.info("请求失败,当前请求参数不安全!请求地址:\n" + requestUrl + "\n不安全参数:" + pname + ":" + value);
|
||||
return false;
|
||||
}
|
||||
if (SafeUtil.checkSpecial(value) || SafeUtil.checkScript(value)) {
|
||||
log.info("请求失败,当前请求参数包含特殊字符!请求地址:\n" + requestUrl + "\n特殊字符参数:" + pname + ":" + value);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过rnd随机参数判断相同url是否多次请求访问
|
||||
*
|
||||
* @param currentRequest
|
||||
* @param requestWrapper
|
||||
* @return
|
||||
*/
|
||||
private boolean checkSameUrl(String currentRequest, XssRequestWrapper requestWrapper) {
|
||||
if (rnd != null && rnd.length() > 0) {
|
||||
try {
|
||||
double newRnd = Double.parseDouble(rnd);
|
||||
/**
|
||||
* 当为空时,新增请求时间记录,第一次请求,放行,允许查询
|
||||
*/
|
||||
HttpSession session = requestWrapper.getSession();
|
||||
Object obj = session.getAttribute("requestLogMap");
|
||||
if (obj == null) {
|
||||
requestLogMap = new HashMap<String, List<Double>>(100);
|
||||
} else {
|
||||
requestLogMap = (Map<String, List<Double>>) obj;
|
||||
}
|
||||
|
||||
List<Double> list = requestLogMap.get(currentRequest);
|
||||
if (list == null) {
|
||||
list = new ArrayList<Double>();
|
||||
} else {
|
||||
for (Double oldRnd : list) {
|
||||
if (oldRnd == newRnd) {
|
||||
log.info("请求失败,当前请求已过期!请重新登录!您的请求地址:\n" + currentRequest);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
list.add(newRnd);
|
||||
requestLogMap.put(currentRequest, list);
|
||||
String brute = "requestLogMap";
|
||||
if (session.getAttribute(brute) != null) {
|
||||
session.removeAttribute(brute);
|
||||
}
|
||||
session.setAttribute(brute, requestLogMap);
|
||||
} catch (NumberFormatException e) {
|
||||
log.info("rnd参数格式化错误 请检查");
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
log.info("请求失败,rnd参数不存在!请重新登录!您的请求地址:\n" + currentRequest);
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查所有页面输入的参数是否安全 request.getReader()
|
||||
*
|
||||
* @param readerParam
|
||||
* @param requestUrl
|
||||
*/
|
||||
private boolean checkReader(String readerParam, String requestUrl) {
|
||||
if (SafeUtil.checkScript(readerParam) || SafeUtil.checkSafeSql(readerParam)) {
|
||||
log.info("请求失败,当前请求参数不安全!请求地址:\n" + requestUrl + "\n不安全参数:数据流:" + readerParam);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
|
||||
throws Exception {
|
||||
// SecurityContextHolder.remove();
|
||||
}
|
||||
}
|
||||
|
|
@ -25,35 +25,42 @@ import java.util.regex.Pattern;
|
|||
public class XssRequestWrapper extends HttpServletRequestWrapper {
|
||||
|
||||
private byte[] body;
|
||||
|
||||
private String queryString;
|
||||
|
||||
private String streamParam;
|
||||
|
||||
private boolean checked;
|
||||
|
||||
public XssRequestWrapper(HttpServletRequest request) {
|
||||
super(request);
|
||||
getParameterMap();
|
||||
BufferedReader reader;
|
||||
|
||||
// 先读取请求体数据
|
||||
try {
|
||||
reader = request.getReader();
|
||||
StringBuilder sb = new StringBuilder();
|
||||
char[] buf = new char[1024];
|
||||
int rd;
|
||||
while ((rd = reader.read(buf)) != -1) {
|
||||
sb.append(buf, 0, rd);
|
||||
// 读取请求体内容
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
BufferedReader reader = request.getReader();
|
||||
char[] charBuffer = new char[1024];
|
||||
int bytesRead;
|
||||
while ((bytesRead = reader.read(charBuffer)) > 0) {
|
||||
stringBuilder.append(charBuffer, 0, bytesRead);
|
||||
}
|
||||
reader.close();
|
||||
streamParam = xssClean(sb.toString());
|
||||
setChecked(xssCleanNew(sb.toString()) && xssCleanNew(request.getQueryString()));
|
||||
body = streamParam.getBytes();
|
||||
String requestBody = stringBuilder.toString();
|
||||
|
||||
// 进行XSS清理
|
||||
streamParam = xssClean(requestBody);
|
||||
body = streamParam.getBytes(request.getCharacterEncoding() != null ?
|
||||
request.getCharacterEncoding() : "UTF-8");
|
||||
|
||||
// 检查安全性
|
||||
String queryStr = request.getQueryString();
|
||||
setChecked(xssCleanNew(requestBody) && (queryStr == null || xssCleanNew(queryStr)));
|
||||
|
||||
} catch (IOException e) {
|
||||
log.error(e.getLocalizedMessage(),e);
|
||||
log.error("读取请求体数据失败: {}", e.getLocalizedMessage(), e);
|
||||
body = new byte[0];
|
||||
streamParam = "";
|
||||
setChecked(false);
|
||||
}
|
||||
|
||||
queryString = xssClean(request.getQueryString());
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -63,135 +70,132 @@ public class XssRequestWrapper extends HttpServletRequestWrapper {
|
|||
|
||||
@Override
|
||||
public BufferedReader getReader() throws IOException {
|
||||
return new BufferedReader(new InputStreamReader(getInputStream()));
|
||||
return new BufferedReader(new InputStreamReader(getInputStream(), getCharacterEncoding()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ServletInputStream getInputStream() throws IOException {
|
||||
|
||||
final ByteArrayInputStream bais = new ByteArrayInputStream(body);
|
||||
return new ServletInputStream() {
|
||||
@Override
|
||||
public boolean isFinished() {
|
||||
return false;
|
||||
return bais.available() == 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isReady() {
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setReadListener(ReadListener listener) {
|
||||
|
||||
// 不需要实现
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read() throws IOException {
|
||||
int read = bais.read();
|
||||
bais.close();
|
||||
return read;
|
||||
return bais.read();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String[]> getParameterMap() {
|
||||
Map<String, String[]> requestMap = super.getParameterMap();
|
||||
if(requestMap!=null){
|
||||
Iterator iterator = requestMap.entrySet().iterator();
|
||||
if (requestMap != null) {
|
||||
Iterator<Map.Entry<String, String[]>> iterator = requestMap.entrySet().iterator();
|
||||
while (iterator.hasNext()) {
|
||||
Map.Entry me = (Map.Entry) iterator.next();
|
||||
String[] values = (String[]) me.getValue();
|
||||
Map.Entry<String, String[]> entry = iterator.next();
|
||||
String[] values = entry.getValue();
|
||||
for (int i = 0; i < values.length; i++) {
|
||||
if (values[i] != null) {
|
||||
values[i] = xssClean(values[i]);
|
||||
}
|
||||
}
|
||||
}}
|
||||
}
|
||||
}
|
||||
return requestMap;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getParameterValues(String paramString) {
|
||||
String[] arrayOfString1 = super.getParameterValues(paramString);
|
||||
if (arrayOfString1 == null){
|
||||
String[] values = super.getParameterValues(paramString);
|
||||
if (values == null) {
|
||||
return null;
|
||||
}
|
||||
int i = arrayOfString1.length;
|
||||
String[] arrayOfString2 = new String[i];
|
||||
|
||||
for (int j = 0; j < i; j++) {
|
||||
if (arrayOfString1[j] != null) {
|
||||
arrayOfString2[j] = xssClean(arrayOfString1[j]);
|
||||
String[] cleanedValues = new String[values.length];
|
||||
for (int i = 0; i < values.length; i++) {
|
||||
if (values[i] != null) {
|
||||
cleanedValues[i] = xssClean(values[i]);
|
||||
}
|
||||
}
|
||||
return arrayOfString2;
|
||||
return cleanedValues;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getParameter(String paramString) {
|
||||
String str = super.getParameter(paramString);
|
||||
if (str == null){
|
||||
String value = super.getParameter(paramString);
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
return xssClean(str);
|
||||
return xssClean(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHeader(String paramString) {
|
||||
String str = super.getHeader(paramString);
|
||||
if (str == null){
|
||||
String value = super.getHeader(paramString);
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
return xssClean(str);
|
||||
return xssClean(value);
|
||||
}
|
||||
String regex1="<script>(.*?)</script>";
|
||||
String regex2="src[\r\n]*=[\r\n]*'(.*?)'";
|
||||
String regex3="src[\r\n]*=[\r\n]*\"(.*?)\"";
|
||||
String regex4="</script>";
|
||||
String regex5="<script(.*?)>";
|
||||
String regex6="eval\\((.*?)\\)";
|
||||
String regex7="e-xpression\\((.*?)\\)";
|
||||
String regex8="javascript:";
|
||||
String regex9="vbscript:";
|
||||
String regex10="onload(.*?)=";
|
||||
|
||||
// 正则表达式模式
|
||||
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(.*?)=";
|
||||
|
||||
// 添加安全脚本模式
|
||||
public static final String SAFE_SCRIPT_PATTERN = "(\\||;|\\$|'|\\'|0x0d|0x0a|\\%27|\\%3B|<>|\\[\\]|\\(\\)|/|\"|script|alert|svg|confirm|prompt|onload|%3c|%3e|%2b|@|!|img|src)";
|
||||
|
||||
private String xssClean(String value) {
|
||||
if (value == null) {
|
||||
return value;
|
||||
return null;
|
||||
}
|
||||
value = value.replaceAll("", "");
|
||||
|
||||
Pattern scriptPattern = Pattern.compile(regex1, Pattern.CASE_INSENSITIVE);
|
||||
value = scriptPattern.matcher(value).replaceAll("");
|
||||
scriptPattern = Pattern.compile(regex2, Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
|
||||
value = scriptPattern.matcher(value).replaceAll("");
|
||||
scriptPattern = Pattern.compile(regex3, Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
|
||||
value = scriptPattern.matcher(value).replaceAll("");
|
||||
scriptPattern = Pattern.compile(regex4, Pattern.CASE_INSENSITIVE);
|
||||
value = scriptPattern.matcher(value).replaceAll("");
|
||||
scriptPattern = Pattern.compile(regex5, Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
|
||||
value = scriptPattern.matcher(value).replaceAll("");
|
||||
scriptPattern = Pattern.compile(regex6, Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
|
||||
value = scriptPattern.matcher(value).replaceAll("");
|
||||
scriptPattern = Pattern.compile(regex7, Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
|
||||
value = scriptPattern.matcher(value).replaceAll("");
|
||||
scriptPattern = Pattern.compile(regex8, Pattern.CASE_INSENSITIVE);
|
||||
value = scriptPattern.matcher(value).replaceAll("");
|
||||
scriptPattern = Pattern.compile(regex9, Pattern.CASE_INSENSITIVE);
|
||||
value = scriptPattern.matcher(value).replaceAll("");
|
||||
scriptPattern = Pattern.compile(regex10, Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
|
||||
value = scriptPattern.matcher(value).replaceAll("");
|
||||
// 移除空字符串替换(这行代码没有实际效果)
|
||||
// value = value.replaceAll("", "");
|
||||
|
||||
// 使用预编译的模式提高性能
|
||||
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("");
|
||||
// 添加额外的安全过滤
|
||||
value = Pattern.compile(SAFE_SCRIPT_PATTERN, Pattern.CASE_INSENSITIVE).matcher(value).replaceAll("");
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
private boolean xssCleanNew(String value) {
|
||||
boolean find = false;
|
||||
if (value == null) {
|
||||
return true;
|
||||
}
|
||||
List<Pattern> patterns = new ArrayList<Pattern>();
|
||||
|
||||
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));
|
||||
|
|
@ -202,29 +206,21 @@ public class XssRequestWrapper extends HttpServletRequestWrapper {
|
|||
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));
|
||||
patterns.add(Pattern.compile(SAFE_SCRIPT_PATTERN, Pattern.CASE_INSENSITIVE));
|
||||
|
||||
for (Pattern pattern : patterns) {
|
||||
find = match(pattern, value) ;
|
||||
if (find) {
|
||||
break;
|
||||
if (match(pattern, value)) {
|
||||
return false; // 发现XSS攻击
|
||||
}
|
||||
|
||||
}
|
||||
return !find;
|
||||
return true; // 安全
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行正则表达式
|
||||
*
|
||||
* @param pattern
|
||||
* 表达式
|
||||
* @param str
|
||||
* 待验证字符串
|
||||
* @return 返回 <b>true </b>,否则为 <b>false </b>
|
||||
* 执行正则表达式匹配
|
||||
*/
|
||||
private static boolean match(Pattern pattern, String str) {
|
||||
Matcher m = pattern.matcher(str);
|
||||
return m.find();
|
||||
return pattern.matcher(str).find();
|
||||
}
|
||||
|
||||
public String getReaderParam() {
|
||||
|
|
@ -238,4 +234,11 @@ public class XssRequestWrapper extends HttpServletRequestWrapper {
|
|||
public void setChecked(boolean checked) {
|
||||
this.checked = checked;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 添加字符编码支持
|
||||
public String getCharacterEncoding() {
|
||||
String encoding = super.getCharacterEncoding();
|
||||
return encoding != null ? encoding : "UTF-8";
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue