mirror of
https://github.com/continew-org/continew-starter.git
synced 2025-09-12 14:57:10 +08:00
feat(web): 新增 XSS 过滤器
This commit is contained in:
@@ -79,6 +79,11 @@ public class PropertiesConstants {
|
|||||||
*/
|
*/
|
||||||
public static final String TRACE = WEB + ".trace";
|
public static final String TRACE = WEB + ".trace";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 链路配置
|
||||||
|
*/
|
||||||
|
public static final String XSS = WEB + ".xss";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 日志配置
|
* 日志配置
|
||||||
*/
|
*/
|
||||||
|
@@ -0,0 +1,44 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||||
|
* <p>
|
||||||
|
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* <p>
|
||||||
|
* http://www.gnu.org/licenses/lgpl.html
|
||||||
|
* <p>
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package top.charles7c.continew.starter.web.autoconfigure.xss;
|
||||||
|
|
||||||
|
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
|
||||||
|
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||||
|
import org.springframework.boot.web.servlet.FilterRegistrationBean;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import top.charles7c.continew.starter.core.constant.PropertiesConstants;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* XSS配置
|
||||||
|
*
|
||||||
|
* @author whhya
|
||||||
|
* @since 1.0.0
|
||||||
|
*/
|
||||||
|
@AutoConfiguration
|
||||||
|
@ConditionalOnWebApplication
|
||||||
|
@ConditionalOnProperty(prefix = PropertiesConstants.XSS, name = PropertiesConstants.ENABLED, havingValue = "true")
|
||||||
|
@EnableConfigurationProperties(XssProperties.class)
|
||||||
|
public class XssAutoConfiguration {
|
||||||
|
@Bean
|
||||||
|
public FilterRegistrationBean<XssFilter> XssFilter(XssProperties xssProperties) {
|
||||||
|
FilterRegistrationBean<XssFilter> registrationBean = new FilterRegistrationBean<>();
|
||||||
|
registrationBean.setFilter(new XssFilter(xssProperties));
|
||||||
|
return registrationBean;
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,110 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||||
|
* <p>
|
||||||
|
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* <p>
|
||||||
|
* http://www.gnu.org/licenses/lgpl.html
|
||||||
|
* <p>
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package top.charles7c.continew.starter.web.autoconfigure.xss;
|
||||||
|
|
||||||
|
import cn.hutool.core.collection.CollectionUtil;
|
||||||
|
import jakarta.servlet.*;
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.http.server.PathContainer;
|
||||||
|
import org.springframework.web.util.pattern.PathPattern;
|
||||||
|
import org.springframework.web.util.pattern.PathPatternParser;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* xss过滤器
|
||||||
|
*
|
||||||
|
* @author whhya
|
||||||
|
* @since 1.0.0
|
||||||
|
*/
|
||||||
|
public class XssFilter implements Filter {
|
||||||
|
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(XssFilter.class);
|
||||||
|
|
||||||
|
private final XssProperties xssProperties;
|
||||||
|
|
||||||
|
public XssFilter(XssProperties xssProperties) {
|
||||||
|
this.xssProperties = xssProperties;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void init(FilterConfig filterConfig) {
|
||||||
|
log.debug("[ContiNew Starter] - Auto Configuration 'Web-XssFilter' completed initialization.");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void doFilter(ServletRequest servletRequest,
|
||||||
|
ServletResponse servletResponse,
|
||||||
|
FilterChain filterChain) throws IOException, ServletException {
|
||||||
|
HttpServletRequest req = (HttpServletRequest) servletRequest;
|
||||||
|
//未开启xss过滤,则直接跳过
|
||||||
|
if (!xssProperties.isEnabled()) {
|
||||||
|
filterChain.doFilter(req, servletResponse);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//限定url地址
|
||||||
|
List<String> pathPatterns = xssProperties.getPathPatterns();
|
||||||
|
|
||||||
|
//判断是否匹配需要忽略地址
|
||||||
|
List<String> pathExcludePatterns = xssProperties.getPathExcludePatterns();
|
||||||
|
if (CollectionUtil.isNotEmpty(pathPatterns)) {
|
||||||
|
if (isMatchPath(req.getServletPath(), pathExcludePatterns)) {
|
||||||
|
filterChain.doFilter(req, servletResponse);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//如果存在则限定path拦截
|
||||||
|
if (CollectionUtil.isNotEmpty(pathPatterns)) {
|
||||||
|
//未匹配上限定地址,则直接不过滤
|
||||||
|
if (isMatchPath(req.getServletPath(), pathPatterns)) {
|
||||||
|
filterChain.doFilter(new XssServletRequestWrapper(req), servletResponse);
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
filterChain.doFilter(req, servletResponse);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//默认拦截
|
||||||
|
filterChain.doFilter(new XssServletRequestWrapper((HttpServletRequest) servletRequest), servletResponse);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断数组中是否存在匹配的路径
|
||||||
|
*
|
||||||
|
* @param requestURL 请求地址
|
||||||
|
* @param pathPatterns 指定匹配路径
|
||||||
|
* @return true 匹配 false 不匹配
|
||||||
|
*/
|
||||||
|
private static boolean isMatchPath(String requestURL, List<String> pathPatterns) {
|
||||||
|
for (String pattern : pathPatterns) {
|
||||||
|
PathPattern pathPattern = PathPatternParser.defaultInstance.parse(pattern);
|
||||||
|
PathContainer pathContainer = PathContainer.parsePath(requestURL);
|
||||||
|
boolean matches = pathPattern.matches(pathContainer);
|
||||||
|
if (matches) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,72 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||||
|
* <p>
|
||||||
|
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* <p>
|
||||||
|
* http://www.gnu.org/licenses/lgpl.html
|
||||||
|
* <p>
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package top.charles7c.continew.starter.web.autoconfigure.xss;
|
||||||
|
|
||||||
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
|
import top.charles7c.continew.starter.core.constant.PropertiesConstants;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* xss配置属性
|
||||||
|
*
|
||||||
|
* @author whhya
|
||||||
|
* @since 1.0.0
|
||||||
|
*/
|
||||||
|
@ConfigurationProperties(PropertiesConstants.XSS)
|
||||||
|
public class XssProperties {
|
||||||
|
/**
|
||||||
|
* 是否启用Xss
|
||||||
|
*/
|
||||||
|
private boolean enabled = true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 拦截的路由,默认为空
|
||||||
|
*/
|
||||||
|
private List<String> pathPatterns = new ArrayList<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 放行的路由,默认为空
|
||||||
|
*/
|
||||||
|
private List<String> pathExcludePatterns = new ArrayList<>();
|
||||||
|
|
||||||
|
public boolean isEnabled() {
|
||||||
|
return enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEnabled(boolean enabled) {
|
||||||
|
this.enabled = enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getPathPatterns() {
|
||||||
|
return pathPatterns;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPathPatterns(List<String> pathPatterns) {
|
||||||
|
this.pathPatterns = pathPatterns;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getPathExcludePatterns() {
|
||||||
|
return pathExcludePatterns;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPathExcludePatterns(List<String> pathExcludePatterns) {
|
||||||
|
this.pathExcludePatterns = pathExcludePatterns;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,126 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||||
|
* <p>
|
||||||
|
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* <p>
|
||||||
|
* http://www.gnu.org/licenses/lgpl.html
|
||||||
|
* <p>
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package top.charles7c.continew.starter.web.autoconfigure.xss;
|
||||||
|
|
||||||
|
import cn.hutool.core.io.IoUtil;
|
||||||
|
import cn.hutool.core.util.ArrayUtil;
|
||||||
|
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 java.io.BufferedReader;
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.StringReader;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 针对XssServletRequest进行过滤的包装类
|
||||||
|
*
|
||||||
|
* @author whh
|
||||||
|
* @since 1.0.0
|
||||||
|
*/
|
||||||
|
public class XssServletRequestWrapper extends HttpServletRequestWrapper {
|
||||||
|
|
||||||
|
private String body = "";
|
||||||
|
String[] method = {"POST", "PATCH", "PUT"};
|
||||||
|
|
||||||
|
public XssServletRequestWrapper(HttpServletRequest request) throws IOException {
|
||||||
|
super(request);
|
||||||
|
if (StrUtil.containsAny(request.getMethod().toUpperCase(), method)) {
|
||||||
|
body = IoUtil.getReader(request.getReader()).readLine();
|
||||||
|
if (StrUtil.isNotEmpty(body)) {
|
||||||
|
body = cleanHtmlTag(body);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BufferedReader getReader() {
|
||||||
|
return IoUtil.toBuffered(new StringReader(body));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ServletInputStream getInputStream() {
|
||||||
|
return getServletInputStream(body);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getQueryString() {
|
||||||
|
return cleanHtmlTag(super.getQueryString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getParameter(String name) {
|
||||||
|
return cleanHtmlTag(super.getParameter(name));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String[] getParameterValues(String name) {
|
||||||
|
String[] values = super.getParameterValues(name);
|
||||||
|
if (ArrayUtil.isEmpty(values)) {
|
||||||
|
return values;
|
||||||
|
}
|
||||||
|
int length = values.length;
|
||||||
|
String[] escapeValues = new String[length];
|
||||||
|
for (int i = 0; i < length; i++) {
|
||||||
|
escapeValues[i] = cleanHtmlTag(values[i]);
|
||||||
|
}
|
||||||
|
return escapeValues;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清除文本中所有HTML标签,但是不删除标签内的内容
|
||||||
|
*
|
||||||
|
* @param content 文本内容
|
||||||
|
* @return 处理过后的文本
|
||||||
|
*/
|
||||||
|
private String cleanHtmlTag(String content) {
|
||||||
|
if (StrUtil.isEmpty(content)) {
|
||||||
|
return content;
|
||||||
|
}
|
||||||
|
return HtmlUtil.cleanHtmlTag(content);
|
||||||
|
}
|
||||||
|
|
||||||
|
static ServletInputStream getServletInputStream(String body) {
|
||||||
|
final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(body.getBytes());
|
||||||
|
return new ServletInputStream() {
|
||||||
|
public int read() {
|
||||||
|
return byteArrayInputStream.read();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isFinished() {
|
||||||
|
return byteArrayInputStream.available() == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isReady() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setReadListener(ReadListener readListener) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
@@ -1,2 +1,3 @@
|
|||||||
top.charles7c.continew.starter.web.autoconfigure.cors.CorsAutoConfiguration
|
top.charles7c.continew.starter.web.autoconfigure.cors.CorsAutoConfiguration
|
||||||
top.charles7c.continew.starter.web.autoconfigure.trace.TraceAutoConfiguration
|
top.charles7c.continew.starter.web.autoconfigure.trace.TraceAutoConfiguration
|
||||||
|
top.charles7c.continew.starter.web.autoconfigure.xss.XssAutoConfiguration
|
Reference in New Issue
Block a user