您现在的位置是:首页 > 技术人生 > 后端技术后端技术
SpringBoot添加XSS过滤器
高晓波2021-09-14【后端技术】人已围观
简介XSS攻击大家应该不太陌生,主要是通过向后台提交恶意代码,实现自己不可告人的秘密。
那么怎么实现XSS过滤呢?网上看了一圈,很多文章说通过Filter + 重写JsonDeserializer实现对请求参数的拦截、过滤。该方法本身没毛病,可惜我做得这个项目有富文本编辑器,重写JsonDeserializer的方式相当于对json参数一刀切,而对于富文本编辑器传过来的参数我是需要放行的。
XSS攻击大家应该不太陌生,主要是通过向后台提交恶意代码,实现自己不可告人的秘密。
那么怎么实现XSS过滤呢?网上看了一圈,很多文章说通过Filter + 重写JsonDeserializer实现对请求参数的拦截、过滤。该方法本身没毛病,可惜我做得这个项目有富文本编辑器,重写JsonDeserializer的方式相当于对json参数一刀切,而对于富文本编辑器传过来的参数我是需要放行的。
所以本篇讲一下通过纯Filter的方式,实现一个XSS过滤器,以达到对部分url进行过滤,对部分url放行。放行的url毕竟是少数,再在其方法中进行精准控制,如:标题、作者等字段手工过滤,正文字段不过滤。
至此,一个粒度为URL级别的XSS过滤器就搞定了。对于上述忽略的url如果并非所有请求参数都需要忽略,只需要在方法中手工过滤即可。
那么怎么实现XSS过滤呢?网上看了一圈,很多文章说通过Filter + 重写JsonDeserializer实现对请求参数的拦截、过滤。该方法本身没毛病,可惜我做得这个项目有富文本编辑器,重写JsonDeserializer的方式相当于对json参数一刀切,而对于富文本编辑器传过来的参数我是需要放行的。
所以本篇讲一下通过纯Filter的方式,实现一个XSS过滤器,以达到对部分url进行过滤,对部分url放行。放行的url毕竟是少数,再在其方法中进行精准控制,如:标题、作者等字段手工过滤,正文字段不过滤。
1、XSS过滤工具类
import java.util.Objects;
/**
* @author gaoxiaobo
* @description
* @date 2021/9/13 1:30 下午
*/
public class XssUtil {
public static String cleanXSS(String value) {
if (Objects.isNull(value)) {
return value;
}
//在这里自定义需要过滤的字符
value = value.replaceAll("<", "<").replaceAll(">", ">");
value = value.replaceAll("\\(", "(").replaceAll("\\)", ")");
value = value.replaceAll("'", "'");
value = value.replaceAll("eval((.*))", "");
value = value.replaceAll("[\"'][s]*javascript:(.*)[\"']", "\"\"");
value = value.replaceAll("<script>", "");
return value;
}
}
2、XSS请求处理 XssHttpServletRequestWrapper
import com.gaoxiaobo.tms.utils.XssUtil;
import com.google.common.net.HttpHeaders;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.MediaType;
import org.springframework.web.servlet.HandlerMapping;
import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import static java.nio.charset.StandardCharsets.UTF_8;
/**
* @author gaoxiaobo
* @description
* @date 2021/9/13 1:29 下午
*/
public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper {
public XssHttpServletRequestWrapper(HttpServletRequest request) {
super(request);
}
/**
* 对header处理
* @param name
* @return
*/
@Override
public String getHeader(String name) {
String value = super.getHeader(name);
return XssUtil.cleanXSS(value);
}
/**
* 对参数处理
* @param name
* @return
*/
@Override
public String getParameter(String name) {
String value = super.getParameter(name);
return XssUtil.cleanXSS(value);
}
/**
* 对数值进行处理
* @param name
* @return
*/
@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++) {
escapseValues[i] = XssUtil.cleanXSS(values[i]);
}
return escapseValues;
}
return super.getParameterValues(name);
}
/**
* 主要是针对HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE 获取pathvalue的时候把原来的pathvalue经过xss过滤掉
*/
@Override
public Object getAttribute(String name) {
// 获取pathvalue的值
if (HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE.equals(name)) {
Map uriTemplateVars = (Map) super.getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE);
if (Objects.isNull(uriTemplateVars)) {
return uriTemplateVars;
}
Map newMap = new LinkedHashMap<>();
uriTemplateVars.forEach((key, value) -> {
if (value instanceof String) {
newMap.put(key, XssUtil.cleanXSS((String) value));
} else {
newMap.put(key, value);
}
});
return newMap;
} else {
return super.getAttribute(name);
}
}
/**
* 对json处理
* @return
* @throws IOException
*/
@Override
public ServletInputStream getInputStream() throws IOException {
// 非json类型,直接返回
if (!isJsonRequest()) {
return super.getInputStream();
}
List<String> lines = IOUtils.readLines(super.getInputStream(), UTF_8);
String json = String.join("\n", lines);
// 为空,直接返回
if (StringUtils.isBlank(json)) {
return super.getInputStream();
}
// xss过滤
json = XssUtil.cleanXSS(json);
final ByteArrayInputStream bis = new ByteArrayInputStream(json.getBytes(UTF_8));
return new ServletInputStream() {
@Override
public boolean isFinished(){
return true;
}
@Override
public boolean isReady(){
return true;
}
@Override
public void setReadListener(ReadListener readListener) {
}
@Override
public int read(){
return bis.read();
}
};
}
/**
* 是否是Json请求
*/
private boolean isJsonRequest() {
String header = super.getHeader(HttpHeaders.CONTENT_TYPE);
return MediaType.APPLICATION_JSON_VALUE.equalsIgnoreCase(header) || MediaType.APPLICATION_JSON_UTF8_VALUE.equalsIgnoreCase(header);
}
}
3、XSS过滤器XSSFilter
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* @author gaoxiaobo
* @description
* @date 2021/9/13 1:34 下午
*/
@Component
public class XSSFilter implements Filter {
/**
* 排除链接
*/
@Value("#{'${xss.excludes}'.split(',')}")
private List<String> excludes;
@Value("${xss.enabled}")
private Boolean enabled;
@Override
public void doFilter(ServletRequest req, ServletResponse res,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest request;
/**
* 判断是否进行xss过滤
*/
if (!handleExcludeUrl(req)){
request = new XssHttpServletRequestWrapper((HttpServletRequest) req);
}else {
request = (HttpServletRequest) req;
}
chain.doFilter(request, response);
}
public void init(FilterConfig filterConfig) {
}
public void destroy() {
}
/**
* 判断当前路径是否需要过滤
*/
private boolean handleExcludeUrl(ServletRequest request) {
if (!enabled) {
return true;
}
if (excludes == null || excludes.isEmpty()) {
return false;
}
HttpServletRequest req = (HttpServletRequest)request;
String url = req.getServletPath();
for (String pattern : excludes) {
Pattern p = Pattern.compile("^" + pattern);
Matcher m = p.matcher(url);
if (m.find()) {
return true;
}
}
return false;
}
}
4、在application.yml添加xss配置
xss:
enabled: true
excludes: /cms/article/create-save,/cms/article/create-publish,/cms/article/edit-save,/cms/article/edit-publish
至此,一个粒度为URL级别的XSS过滤器就搞定了。对于上述忽略的url如果并非所有请求参数都需要忽略,只需要在方法中手工过滤即可。
Tags:java安全防护
很赞哦! ()
相关文章
随机图文
-
【转】GPT 应用开发和思考
在过去几个月的时间中,我们似乎正处于人工智能的革命中。除了大多数人了解的 OpenAI ChatGPT 之外,许多非常新颖、有趣、实用的 AI 应用也是层出不穷,并且在使用这些应用时时,笔者也确确实实的感受到了生产力的提高。 -
mybatis plus生成实体entity没有id
总是写很多重复的增删改查毫无意义,浪费生命,于是乎准备弄一个增删改查代码生成工具。网上查了一下mybatis plus挺好的,于是乎打开官网 Quick Start!按照官网的演示栗子:// 演示 -
maven安装jar到本地仓库
JAVA做淘宝客程序开发的时候,我用的是maven管理依赖jar包。 如何将阿里妈妈下载的jar包以及source jar安装到maven本地仓库呢? -
Springboot集成quartz定时任务可视化配置
使用quartz定时任务已经有一段时间了,今天记录一下Springboot 2.x集成Quartz。