diff --git a/continew-starter-web/src/main/java/top/charles7c/continew/starter/web/autoconfigure/xss/XssFilter.java b/continew-starter-web/src/main/java/top/charles7c/continew/starter/web/autoconfigure/xss/XssFilter.java index 3dac5d9c..d0d00d23 100644 --- a/continew-starter-web/src/main/java/top/charles7c/continew/starter/web/autoconfigure/xss/XssFilter.java +++ b/continew-starter-web/src/main/java/top/charles7c/continew/starter/web/autoconfigure/xss/XssFilter.java @@ -65,14 +65,14 @@ public class XssFilter implements Filter { List includePatterns = xssProperties.getIncludePatterns(); if (CollectionUtil.isNotEmpty(includePatterns)) { if (isMatchPath(request.getServletPath(), includePatterns)) { - filterChain.doFilter(new XssServletRequestWrapper(request), servletResponse); + filterChain.doFilter(new XssServletRequestWrapper(request, xssProperties), servletResponse); } else { filterChain.doFilter(request, servletResponse); } return; } // 默认:执行 XSS 过滤 - filterChain.doFilter(new XssServletRequestWrapper(request), servletResponse); + filterChain.doFilter(new XssServletRequestWrapper(request, xssProperties), servletResponse); return; } filterChain.doFilter(servletRequest, servletResponse); diff --git a/continew-starter-web/src/main/java/top/charles7c/continew/starter/web/autoconfigure/xss/XssProperties.java b/continew-starter-web/src/main/java/top/charles7c/continew/starter/web/autoconfigure/xss/XssProperties.java index 0c4e7df3..cb0f3009 100644 --- a/continew-starter-web/src/main/java/top/charles7c/continew/starter/web/autoconfigure/xss/XssProperties.java +++ b/continew-starter-web/src/main/java/top/charles7c/continew/starter/web/autoconfigure/xss/XssProperties.java @@ -18,6 +18,7 @@ package top.charles7c.continew.starter.web.autoconfigure.xss; import org.springframework.boot.context.properties.ConfigurationProperties; import top.charles7c.continew.starter.core.constant.PropertiesConstants; +import top.charles7c.continew.starter.web.enums.XssMode; import java.util.ArrayList; import java.util.List; @@ -50,6 +51,13 @@ public class XssProperties { */ private List excludePatterns = new ArrayList<>(); + /** + * xss过滤方式 + * clean : 删除xss匹配标签 (默认) + * escape : 转义xss匹配标签 + */ + private XssMode mode = XssMode.CLEAN; + public boolean isEnabled() { return enabled; } @@ -73,4 +81,12 @@ public class XssProperties { public void setExcludePatterns(List excludePatterns) { this.excludePatterns = excludePatterns; } + + public XssMode getMode() { + return mode; + } + + public void setMode(XssMode mode) { + this.mode = mode; + } } diff --git a/continew-starter-web/src/main/java/top/charles7c/continew/starter/web/autoconfigure/xss/XssServletRequestWrapper.java b/continew-starter-web/src/main/java/top/charles7c/continew/starter/web/autoconfigure/xss/XssServletRequestWrapper.java index b28e7f62..0957a190 100644 --- a/continew-starter-web/src/main/java/top/charles7c/continew/starter/web/autoconfigure/xss/XssServletRequestWrapper.java +++ b/continew-starter-web/src/main/java/top/charles7c/continew/starter/web/autoconfigure/xss/XssServletRequestWrapper.java @@ -16,19 +16,24 @@ package top.charles7c.continew.starter.web.autoconfigure.xss; +import cn.hutool.core.collection.CollectionUtil; import cn.hutool.core.io.IoUtil; import cn.hutool.core.util.ArrayUtil; +import cn.hutool.core.util.EscapeUtil; +import cn.hutool.core.util.ReUtil; 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 top.charles7c.continew.starter.web.enums.XssMode; import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.StringReader; +import java.util.List; /** * 针对 XssServletRequest 进行过滤的包装类 @@ -39,17 +44,22 @@ import java.io.StringReader; public class XssServletRequestWrapper extends HttpServletRequestWrapper { private String body = ""; + + private final static String ESCAPE_MODE = "ESCAPE"; String[] method = {"POST", "PATCH", "PUT"}; - public XssServletRequestWrapper(HttpServletRequest request) throws IOException { + private final XssProperties xssProperties; + + public XssServletRequestWrapper(HttpServletRequest request, XssProperties xssProperties) throws IOException { super(request); + this.xssProperties = xssProperties; if (StrUtil.containsAny(request.getMethod().toUpperCase(), method)) { body = IoUtil.getReader(request.getReader()).readLine(); - if (StrUtil.isNotEmpty(body)) { - body = cleanHtmlTag(body); + if (StrUtil.isEmpty(body)) { + return; } + body = handleTag(body); } - } @Override @@ -64,12 +74,12 @@ public class XssServletRequestWrapper extends HttpServletRequestWrapper { @Override public String getQueryString() { - return cleanHtmlTag(super.getQueryString()); + return handleTag(super.getQueryString()); } @Override public String getParameter(String name) { - return cleanHtmlTag(super.getParameter(name)); + return handleTag(super.getParameter(name)); } @Override @@ -79,23 +89,41 @@ public class XssServletRequestWrapper extends HttpServletRequestWrapper { return values; } int length = values.length; - String[] escapeValues = new String[length]; + String[] resultValues = new String[length]; for (int i = 0; i < length; i++) { - escapeValues[i] = cleanHtmlTag(values[i]); + resultValues[i] = handleTag(values[i]); } - return escapeValues; + return resultValues; } /** - * 清除文本中所有HTML标签,但是不删除标签内的内容 + * 对文本内容使用指定过滤模式 * * @param content 文本内容 - * @return 处理过后的文本 + * @return 返回处理过后内容 */ - private String cleanHtmlTag(String content) { + private String handleTag(String content) { if (StrUtil.isEmpty(content)) { return content; } + // 获取过滤模式 + XssMode mode = xssProperties.getMode(); + //异常情况下mode为空,则默认用清空模式 + if (StrUtil.isEmpty(mode.name())) { + return HtmlUtil.cleanHtmlTag(content); + } + // 如果是escape模式,则进行转义 + if (mode.name().equals(ESCAPE_MODE)) { + List reStr = ReUtil.findAllGroup0(HtmlUtil.RE_HTML_MARK, content); + if (CollectionUtil.isEmpty(reStr)) { + return content; + } + for (String s : reStr) { + content = content.replace(s, EscapeUtil.escapeHtml4(s).replace("\\", "")); + } + return content; + + } return HtmlUtil.cleanHtmlTag(content); } @@ -123,4 +151,5 @@ public class XssServletRequestWrapper extends HttpServletRequestWrapper { } }; } + } diff --git a/continew-starter-web/src/main/java/top/charles7c/continew/starter/web/enums/XssMode.java b/continew-starter-web/src/main/java/top/charles7c/continew/starter/web/enums/XssMode.java new file mode 100644 index 00000000..5582b844 --- /dev/null +++ b/continew-starter-web/src/main/java/top/charles7c/continew/starter/web/enums/XssMode.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. + *

+ * 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 + *

+ * http://www.gnu.org/licenses/lgpl.html + *

+ * 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.enums; + +/** + * API 类型枚举 + * + * @author whhya + * @since 2.0.0 + */ +public enum XssMode { + + /** + * 所有 API + */ + CLEAN, + /** + * 分页 + */ + ESCAPE, +} \ No newline at end of file