mirror of
				https://github.com/continew-org/continew-starter.git
				synced 2025-10-31 22:57:19 +08:00 
			
		
		
		
	feat(web/xss): XSS 增加过滤模式字段,提供清空、转义 2 种方式,默认使用清空模式
* update:完善XssProperties.mode属性为枚举类型 * feat(web/xss): xss增加过滤模式(mode)字段,提供清空(clean)/转义(escape)2种方式,默认使用clean模式
This commit is contained in:
		| @@ -65,14 +65,14 @@ public class XssFilter implements Filter { | ||||
|             List<String> 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); | ||||
|   | ||||
| @@ -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<String> 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<String> excludePatterns) { | ||||
|         this.excludePatterns = excludePatterns; | ||||
|     } | ||||
|  | ||||
|     public XssMode getMode() { | ||||
|         return mode; | ||||
|     } | ||||
|  | ||||
|     public void setMode(XssMode mode) { | ||||
|         this.mode = mode; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -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<String> 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 { | ||||
|             } | ||||
|         }; | ||||
|     } | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -0,0 +1,35 @@ | ||||
| /* | ||||
|  * 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.enums; | ||||
|  | ||||
| /** | ||||
|  * API 类型枚举 | ||||
|  * | ||||
|  * @author whhya | ||||
|  * @since 2.0.0 | ||||
|  */ | ||||
| public enum XssMode { | ||||
|  | ||||
|     /** | ||||
|      * 所有 API | ||||
|      */ | ||||
|     CLEAN, | ||||
|     /** | ||||
|      * 分页 | ||||
|      */ | ||||
|     ESCAPE, | ||||
| } | ||||
		Reference in New Issue
	
	Block a user
	 whhya
					whhya