From 339cdc6dba36274fb0b0660431a72e967d008317 Mon Sep 17 00:00:00 2001 From: xiongxiaoyang <773861846@qq.com> Date: Tue, 17 May 2022 20:00:21 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0=20XSS=20=E8=BF=87?= =?UTF-8?q?=E6=BB=A4=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../novel/core/config/XssProperties.java | 28 ++++++++ .../xxyopen/novel/core/filter/XssFilter.java | 69 +++++++++++++++++++ .../wrapper/XssHttpServletRequestWrapper.java | 40 +++++++++++ src/main/resources/application.yml | 10 +++ 4 files changed, 147 insertions(+) create mode 100644 src/main/java/io/github/xxyopen/novel/core/config/XssProperties.java create mode 100644 src/main/java/io/github/xxyopen/novel/core/filter/XssFilter.java create mode 100644 src/main/java/io/github/xxyopen/novel/core/wrapper/XssHttpServletRequestWrapper.java diff --git a/src/main/java/io/github/xxyopen/novel/core/config/XssProperties.java b/src/main/java/io/github/xxyopen/novel/core/config/XssProperties.java new file mode 100644 index 0000000..613e090 --- /dev/null +++ b/src/main/java/io/github/xxyopen/novel/core/config/XssProperties.java @@ -0,0 +1,28 @@ +package io.github.xxyopen.novel.core.config; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; + +import java.util.List; + +/** + * Xss 过滤配置属性 + * + * @author xiongxiaoyang + * @date 2022/5/17 + */ +@ConfigurationProperties(prefix = "novel.xss") +@Data +public class XssProperties { + + /** + * 过滤开关 + * */ + private Boolean enabled; + + /** + * 排除链接 + * */ + private List excludes; + +} diff --git a/src/main/java/io/github/xxyopen/novel/core/filter/XssFilter.java b/src/main/java/io/github/xxyopen/novel/core/filter/XssFilter.java new file mode 100644 index 0000000..b1f3942 --- /dev/null +++ b/src/main/java/io/github/xxyopen/novel/core/filter/XssFilter.java @@ -0,0 +1,69 @@ +package io.github.xxyopen.novel.core.filter; + +import com.baomidou.mybatisplus.core.toolkit.CollectionUtils; +import io.github.xxyopen.novel.core.config.XssProperties; +import io.github.xxyopen.novel.core.wrapper.XssHttpServletRequestWrapper; +import jakarta.servlet.*; +import jakarta.servlet.annotation.WebFilter; +import jakarta.servlet.http.HttpServletRequest; +import lombok.RequiredArgsConstructor; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.stereotype.Component; + +import java.io.IOException; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * 防止 XSS 攻击的过滤器 + * + * @author xiongxiaoyang + * @date 2022/5/17 + */ +@Component +@ConditionalOnProperty(value = "novel.xss.enabled",havingValue = "true") +@WebFilter(urlPatterns = "/*", filterName = "xssFilter") +@EnableConfigurationProperties(value = {XssProperties.class}) +@RequiredArgsConstructor +public class XssFilter implements Filter { + + + private final XssProperties xssProperties; + + @Override + public void init(FilterConfig filterConfig) throws ServletException { + Filter.super.init(filterConfig); + } + + @Override + public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { + HttpServletRequest req = (HttpServletRequest) servletRequest; + if (handleExcludeUrl(req)) { + filterChain.doFilter(servletRequest, servletResponse); + return; + } + XssHttpServletRequestWrapper xssRequest = new XssHttpServletRequestWrapper((HttpServletRequest) servletRequest); + filterChain.doFilter(xssRequest, servletResponse); + } + + private boolean handleExcludeUrl(HttpServletRequest request) { + if (CollectionUtils.isEmpty(xssProperties.getExcludes())) { + return false; + } + String url = request.getServletPath(); + for (String pattern : xssProperties.getExcludes()) { + Pattern p = Pattern.compile("^" + pattern); + Matcher m = p.matcher(url); + if (m.find()) { + return true; + } + } + return false; + } + + @Override + public void destroy() { + Filter.super.destroy(); + } +} \ No newline at end of file diff --git a/src/main/java/io/github/xxyopen/novel/core/wrapper/XssHttpServletRequestWrapper.java b/src/main/java/io/github/xxyopen/novel/core/wrapper/XssHttpServletRequestWrapper.java new file mode 100644 index 0000000..b233a01 --- /dev/null +++ b/src/main/java/io/github/xxyopen/novel/core/wrapper/XssHttpServletRequestWrapper.java @@ -0,0 +1,40 @@ +package io.github.xxyopen.novel.core.wrapper; + +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletRequestWrapper; + +import java.util.HashMap; +import java.util.Map; + +/** + * XSS 过滤处理 + * + * @author xiongxiaoyang + * @date 2022/5/17 + */ +public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper { + + private final Map replaceRule = new HashMap<>(); + + public XssHttpServletRequestWrapper(HttpServletRequest request) { + super(request); + replaceRule.put("<", "<"); + replaceRule.put(">", ">"); + } + + @Override + public String[] getParameterValues(String name) { + String[] values = super.getParameterValues(name); + if (values != null) { + int length = values.length; + String[] escapeValues = new String[length]; + for (int i = 0; i < length; i++) { + String raw = values[i]; + int index = i; + replaceRule.forEach((k, v)-> escapeValues[index] = raw.replaceAll(k, v)); + } + return escapeValues; + } + return new String[0]; + } +} diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 1f66e26..49bfd6f 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -54,4 +54,14 @@ novel: # JWT密钥 jwt: secret: E66559580A1ADF48CDD928516062F12E + # XSS 过滤配置 + xss: + # 过滤开关 + enabled: true + # 排除链接 + excludes: + - /system/notice/* + + +