1200字范文,内容丰富有趣,写作的好帮手!
1200字范文 > springboot增加XSS跨站脚本攻击防护功能

springboot增加XSS跨站脚本攻击防护功能

时间:2020-06-08 12:16:44

相关推荐

springboot增加XSS跨站脚本攻击防护功能

参考 文章/u/4407261/blog/3395458

XSS原理

xss攻击的原理是利用前后端校验不严格,用户将攻击代码植入到数据中提交到了后台,当这些数据在网页上被其他用户查看的时候触发攻击

举例:用户提交表单时把地址写成:山东省济南市<script>for(var i=0;i<9999;i++){alert(i)}</script>

上面的数据如果没有在后台做处理,当数据被展示到网页上的时候,会在网页上弹出N个alert框,当然实际攻击肯定是比这个要复杂的多的

SpringBoot防护

1.增加一个Filter类

import mons.lang3.StringUtils;import javax.servlet.*;import javax.servlet.http.HttpServletRequest;import java.io.IOException;import java.util.ArrayList;import java.util.List;import java.util.regex.Matcher;import java.util.regex.Pattern;public class XssFilter implements Filter {private List<String> excludes = new ArrayList<>();private boolean enabled = false;@Overridepublic void init(FilterConfig filterConfig) throws ServletException {String strExcludes = filterConfig.getInitParameter("excludes");String strEnabled = filterConfig.getInitParameter("enabled");//将不需要xss过滤的接口添加到列表中if(StringUtils.isNotEmpty(strExcludes)){String[] urls = strExcludes.split(",");for(String url:urls){excludes.add(url);}}if(StringUtils.isNotEmpty(strEnabled)){enabled = Boolean.valueOf(strEnabled);}}@Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {HttpServletRequest request = (HttpServletRequest) servletRequest;//如果该访问接口在排除列表里面则不拦截if(isExcludeUrl(request.getServletPath())){filterChain.doFilter(servletRequest,servletResponse);return;}//拦截该url并进行xss过滤XssHttpServletRequestWrapper xssHttpServletRequestWrapper = new XssHttpServletRequestWrapper(request);filterChain.doFilter(xssHttpServletRequestWrapper,servletResponse);}@Overridepublic void destroy() {}private boolean isExcludeUrl(String urlPath){if(!enabled){//如果xss开关关闭了,则所有url都不拦截return true;}if(excludes==null||excludes.isEmpty()){return false;}String url = urlPath;for(String pattern:excludes){Pattern p = pile("^"+pattern);Matcher m = p.matcher(url);if(m.find()){return true;}}return false;}}

2.增加一个xssHttpServletRequestWrapper类,这个类重写了获取参数的方法,在获取参数时做了xss替换处理

import javax.servlet.ReadListener;import javax.servlet.ServletInputStream;import java.io.ByteArrayInputStream;import java.io.IOException;import javax.servlet.http.HttpServletRequest;import java.io.BufferedReader;import java.io.InputStreamReader;import java.util.HashMap;import java.util.List;import java.util.Map;import javax.servlet.http.HttpServletRequestWrapper;import mons.io.IOUtils;import mons.lang.StringUtils;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.http.HttpHeaders;import org.springframework.http.MediaType;import com.alibaba.fastjson.JSON;import com.fasterxml.jackson.core.type.TypeReference;import com.fasterxml.jackson.databind.ObjectMapper;import org.springframework.web.util.HtmlUtils;/*** xss过滤包装类*/public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper {private static final Logger logger = LoggerFactory.getLogger(XssHttpServletRequestWrapper.class);/*** Constructs a request object wrapping the given request.** @param request The request to wrap* @throws IllegalArgumentException if the request is null*/public XssHttpServletRequestWrapper(HttpServletRequest request) throws IOException {super(request);}@Overridepublic String getHeader(String name) {String strHeader = super.getHeader(name);if(StringUtils.isEmpty(strHeader)){return strHeader;}// return Jsoup.clean(super.getHeader(name),Whitelist.relaxed());return HtmlUtils.htmlEscape(strHeader,"UTF-8");}@Overridepublic String getParameter(String name) {String strParameter = super.getParameter(name);if(StringUtils.isEmpty(strParameter)){return strParameter;}return HtmlUtils.htmlEscape(strParameter,"UTF-8");}@Overridepublic String[] getParameterValues(String name) {String[] values = super.getParameterValues(name);if(values==null || name.toLowerCase().endsWith("content")){return values;}int length = values.length;String[] escapseValues = new String[length];for(int i = 0;i<length;i++){//过滤一切可能的xss攻击字符串escapseValues[i] = HtmlUtils.htmlEscape(values[i],"UTF-8");if(!StringUtils.equals(escapseValues[i],values[i])){logger.debug("xss字符串过滤前:"+values[i]+"\r\n"+"过滤后:"+escapseValues[i]);}}return escapseValues;}/*** 重写getInputStream方法*/@Overridepublic ServletInputStream getInputStream() throws IOException {logger.debug("参数去空格处理");//非json类型,直接返回if (!super.getHeader(HttpHeaders.CONTENT_TYPE).equalsIgnoreCase(MediaType.APPLICATION_JSON_VALUE)) {return super.getInputStream();}//从输入流中取出body串, 如果为空,直接返回String reqBodyStr = IOUtils.toString(super.getInputStream(), "utf-8");if (StringUtils.isEmpty(reqBodyStr)) {return super.getInputStream();}logger.debug("转化前reqest body:{}", reqBodyStr);//reqBodyStr转为Map对象Map<String, Object> paramMap = new ObjectMapper().readValue(reqBodyStr, new TypeReference<HashMap<String, Object>>() {});//去首尾空格Map<String,Object> trimedMap = recursiveTrim(paramMap);logger.debug("转化后reqest body:" + JSON.toJSONString(trimedMap));//重新构造一个输入流对象byte[] bytes = JSON.toJSONString(trimedMap).getBytes("utf-8");ByteArrayInputStream bis = new ByteArrayInputStream(bytes);return new MyServletInputStream(bis);}/*** 只处理String, List, Map** @param param* @return*/@SuppressWarnings("unchecked")private Map<String,Object> recursiveTrim(Map<String, Object> param) {for (String key : param.keySet()) {Object vo = param.get(key);if (null == vo) {//key对应的值为空continue;}if (vo instanceof String) {if (key.toLowerCase().endsWith("desc")||key.toLowerCase().endsWith("content")||key.toLowerCase().endsWith("introduce")){continue;}//key对应的值为String类型, 过滤后重新放入mapparam.put(key,HtmlUtils.htmlEscape((String) vo,"UTF-8"));} else if (vo instanceof Map) {param.put(key, recursiveTrim((Map<String,Object>) vo));} else if (vo instanceof List) {//key对应的值为List类型List<Object> alist = (List<Object>) vo;for (int i = 0; i < alist.size(); i++) {//遍历listObject vol = alist.get(i);if (vol instanceof String) {//list里的元素为String, 过滤alist.set(i, HtmlUtils.htmlEscape((String) vol,"UTF-8"));} else if (vol instanceof Map) {//list里的元素为Map, 递归处理alist.set(i, recursiveTrim((Map<String,Object>) vol));}}param.put(key, vo);}}return param;}@Overridepublic BufferedReader getReader() throws IOException {return new BufferedReader(new InputStreamReader(getInputStream()));}}class MyServletInputStream extends ServletInputStream {private ByteArrayInputStream bis;public MyServletInputStream(ByteArrayInputStream bis) {this.bis = bis;}@Overridepublic boolean isFinished() {return true;}@Overridepublic boolean isReady() {return true;}@Overridepublic void setReadListener(ReadListener listener) {}@Overridepublic int read() throws IOException {return bis.read();}}

3.SpringBoot里面增加一个configuration配置,把Filter类配置上去

import .filter.XssFilter;import mons.lang.StringUtils;import org.springframework.beans.factory.annotation.Value;import org.springframework.boot.web.servlet.FilterRegistrationBean;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import javax.servlet.DispatcherType;import java.util.HashMap;import java.util.Map;/*** 设置跨站脚本过滤*/@Configurationpublic class FilterConfig {@Value("${xss.enabled}")private String enabled;@Value("${xss.excludes}")private String excludes;@Value("${xss.urlPatterns}")private String urlPatterns;@Beanpublic FilterRegistrationBean xssFilterRegistration(){FilterRegistrationBean registrationBean = new FilterRegistrationBean();registrationBean.setDispatcherTypes(DispatcherType.REQUEST);registrationBean.setFilter(new XssFilter());registrationBean.addUrlPatterns(StringUtils.split(urlPatterns,","));registrationBean.setName("XssFilter");registrationBean.setOrder(9999);Map<String,String> initParameters = new HashMap<>();initParameters.put("excludes",excludes);initParameters.put("enabled",enabled);registrationBean.setInitParameters(initParameters);return registrationBean;}}

4.最后在application.properties或者application.yml里面增加一些开关配置,可以忽略某些接口提交的数据或者关闭xss过滤

#xss攻击拦截xss.enabled=truexss.excludes=xss.urlPatterns=/*

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。