fix 修复 经过加密的请求无法过滤xss问题 将xss实现从gateway移动到common-web解密后过滤

This commit is contained in:
疯狂的狮子Li
2024-10-20 12:26:05 +08:00
parent f64b17b548
commit 503a0efc31
8 changed files with 209 additions and 113 deletions

View File

@@ -0,0 +1,33 @@
package org.dromara.common.web.config;
import jakarta.servlet.DispatcherType;
import org.dromara.common.web.config.properties.XssProperties;
import org.dromara.common.web.filter.XssFilter;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
/**
* Filter配置
*
* @author Lion Li
*/
@AutoConfiguration
@EnableConfigurationProperties(XssProperties.class)
public class FilterConfig {
@Bean
@ConditionalOnProperty(value = "xss.enabled", havingValue = "true")
public FilterRegistrationBean<XssFilter> xssFilterRegistration() {
FilterRegistrationBean<XssFilter> registration = new FilterRegistrationBean<>();
registration.setDispatcherTypes(DispatcherType.REQUEST);
registration.setFilter(new XssFilter());
registration.addUrlPatterns("/*");
registration.setName("xssFilter");
registration.setOrder(FilterRegistrationBean.HIGHEST_PRECEDENCE + 1);
return registration;
}
}

View File

@@ -0,0 +1,30 @@
package org.dromara.common.web.config.properties;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import java.util.ArrayList;
import java.util.List;
/**
* XSS跨站脚本配置
*
* @author Lion Li
*/
@Data
@RefreshScope
@ConfigurationProperties(prefix = "xss")
public class XssProperties {
/**
* Xss开关
*/
private Boolean enabled;
/**
* 排除路径
*/
private List<String> excludeUrls = new ArrayList<>();
}

View File

@@ -0,0 +1,66 @@
package org.dromara.common.web.filter;
import jakarta.servlet.*;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.dromara.common.core.utils.SpringUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.web.config.properties.XssProperties;
import org.springframework.http.HttpMethod;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/**
* 防止XSS攻击的过滤器
*
* @author ruoyi
*/
public class XssFilter implements Filter {
/**
* 排除链接
*/
public List<String> excludes = new ArrayList<>();
@Override
public void init(FilterConfig filterConfig) throws ServletException {
XssProperties properties = SpringUtils.getBean(XssProperties.class);
String appName = SpringUtils.getApplicationName();
String appPath = "/" + StringUtils.substring(appName, appName.indexOf("-") + 1);
List<String> excludeUrls = properties.getExcludeUrls()
.stream()
.filter(x -> StringUtils.startsWith(x, appPath))
.map(x -> x.replaceFirst(appPath, StringUtils.EMPTY))
.toList();
excludes.addAll(excludeUrls);
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse resp = (HttpServletResponse) response;
if (handleExcludeURL(req, resp)) {
chain.doFilter(request, response);
return;
}
XssHttpServletRequestWrapper xssRequest = new XssHttpServletRequestWrapper((HttpServletRequest) request);
chain.doFilter(xssRequest, response);
}
private boolean handleExcludeURL(HttpServletRequest request, HttpServletResponse response) {
String url = request.getServletPath();
String method = request.getMethod();
// GET DELETE 不过滤
if (method == null || HttpMethod.GET.matches(method) || HttpMethod.DELETE.matches(method)) {
return true;
}
return StringUtils.matches(url, excludes);
}
@Override
public void destroy() {
}
}

View File

@@ -0,0 +1,97 @@
package org.dromara.common.web.filter;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HtmlUtil;
import jakarta.servlet.ReadListener;
import jakarta.servlet.ServletInputStream;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletRequestWrapper;
import org.dromara.common.core.utils.StringUtils;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
/**
* XSS过滤处理
*
* @author ruoyi
*/
public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper {
/**
* @param request
*/
public XssHttpServletRequestWrapper(HttpServletRequest request) {
super(request);
}
@Override
public String[] getParameterValues(String name) {
String[] values = super.getParameterValues(name);
if (values != null) {
int length = values.length;
String[] escapseValues = new String[length];
for (int i = 0; i < length; i++) {
// 防xss攻击和过滤前后空格
escapseValues[i] = HtmlUtil.cleanHtmlTag(values[i]).trim();
}
return escapseValues;
}
return super.getParameterValues(name);
}
@Override
public ServletInputStream getInputStream() throws IOException {
// 非json类型直接返回
if (!isJsonRequest()) {
return super.getInputStream();
}
// 为空,直接返回
String json = StrUtil.str(IoUtil.readBytes(super.getInputStream(), false), StandardCharsets.UTF_8);
if (StringUtils.isEmpty(json)) {
return super.getInputStream();
}
// xss过滤
json = HtmlUtil.cleanHtmlTag(json).trim();
byte[] jsonBytes = json.getBytes(StandardCharsets.UTF_8);
final ByteArrayInputStream bis = IoUtil.toStream(jsonBytes);
return new ServletInputStream() {
@Override
public boolean isFinished() {
return true;
}
@Override
public boolean isReady() {
return true;
}
@Override
public int available() throws IOException {
return jsonBytes.length;
}
@Override
public void setReadListener(ReadListener readListener) {
}
@Override
public int read() throws IOException {
return bis.read();
}
};
}
/**
* 是否是Json请求
*/
public boolean isJsonRequest() {
String header = super.getHeader(HttpHeaders.CONTENT_TYPE);
return StringUtils.startsWithIgnoreCase(header, MediaType.APPLICATION_JSON_VALUE);
}
}

View File

@@ -1,3 +1,4 @@
org.dromara.common.web.config.FilterConfig
org.dromara.common.web.config.I18nConfig
org.dromara.common.web.config.UndertowConfig
org.dromara.common.web.config.ResourcesConfig
org.dromara.common.web.config.ResourcesConfig