xss跨站脚本攻击问题最主要是呈如今html页面的脚本被执行致使的结果,可分为两个方便做屏蔽javascript
在前端上传的各个参数后,对其进行转义后再保存至数据库,属于暴力式转义,通常不建议。下面是写的例子html
1.建立HttpServletRequest新对象,覆盖其中的getParameterMap()
方法,其会被ServletModelAttributeMethodProcessor
处理方法参数时被调用,具体的读者可自行分析前端
package com.jing.springboot.test; import java.util.Enumeration; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequestWrapper; import org.springframework.util.MultiValueMap; public class FormHttpRequestWrapper extends HttpServletRequestWrapper { // 采用spring的MultiValueMap集合 private MultiValueMap<String, String> paramsMap; public FormHttpRequestWrapper(HttpServletRequest request) { super(request); } public FormHttpRequestWrapper(HttpServletRequest request, MultiValueMap<String, String> paramMap) { super(request); this.paramsMap = paramMap; } @Override public String getParameter(String name) { String param = super.getParameter(name); return param == null ? paramsMap.getFirst(name) : param; } @Override public Map<String, String[]> getParameterMap() { Map<String, String[]> paramterMap = super.getParameterMap(); Set<Entry<String, List<String>>> mapSets = paramsMap.entrySet(); for (Entry<String, List<String>> mapSet : mapSets) { String key = mapSet.getKey(); List<String> values = mapSet.getValue(); paramterMap.put(key, values.toArray(new String[values.size()])); } return paramterMap; } @Override public Enumeration<String> getParameterNames() { return super.getParameterNames(); } @Override public String[] getParameterValues(String name) { List<String> multiValues = paramsMap.get(name); String[] oldValues = super.getParameterValues(name); Set<String> trueValues = new HashSet<String>(oldValues.length + multiValues.size()); for (String multi : multiValues) { trueValues.add(multi); } for (String old : oldValues) { trueValues.add(old); } return trueValues.toArray(new String[trueValues.size()]); } }
2.建立参数拦截filter类过滤器,对每次的POST请求或者PUT请求做下拦截java
package com.jing.springboot.test; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.Enumeration; import java.util.List; import java.util.Map.Entry; import java.util.Set; import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpInputMessage; import org.springframework.http.MediaType; import org.springframework.http.converter.FormHttpMessageConverter; import org.springframework.http.converter.support.AllEncompassingFormHttpMessageConverter; import org.springframework.util.MultiValueMap; import org.springframework.web.filter.OncePerRequestFilter; import org.springframework.web.util.HtmlUtils; public class HttpContentFormFilter extends OncePerRequestFilter { private List<MediaType> supportMediaTypes = new ArrayList<MediaType>(); private FormHttpMessageConverter messageConverter = new AllEncompassingFormHttpMessageConverter(); public HttpContentFormFilter() { supportMediaTypes.add(MediaType.APPLICATION_FORM_URLENCODED); } @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { String method = request.getMethod(); String contentType = request.getContentType(); if (methodEqual(method) && mediaEqual(contentType)) { HttpInputMessage httpInputMessage = new FormHttpInputMessage(request); // 采用FormHttpMessageConverter对象读取参数集合 MultiValueMap<String, String> springMultiValueMap = messageConverter.read(null, httpInputMessage); // 使用spring自带的HtmlUtils工具类来转义html标签 useSpringHtmlEscape(springMultiValueMap); // 从新构造request对象,将转义后的参数存进去 FormHttpRequestWrapper httpRequestWrapper = new FormHttpRequestWrapper(request, springMultiValueMap); filterChain.doFilter(httpRequestWrapper, response); } else { filterChain.doFilter(request, response); } } private boolean methodEqual(String reqMethod) { if (reqMethod.equals("POST") || reqMethod.equals("PUT")) { return true; } return false; } private boolean mediaEqual(String mediaType) { boolean isSupport = false; for (MediaType type : supportMediaTypes) { isSupport = type.includes(new MediaType(mediaType)); if (isSupport) { break; } } return isSupport; } private void useSpringHtmlEscape(MultiValueMap<String, String> map) { Set<Entry<String, List<String>>> mapEntrySet = map.entrySet(); for (Entry<String, List<String>> mapEntry : mapEntrySet) { mapEntry.setValue(escapeHtml(mapEntry.getValue())); } } private List<String> escapeHtml(List<String> values) { List<String> escapeValues = new ArrayList<String>(values.size()); for (String value : values) { escapeValues.add(HtmlUtils.htmlEscape(value)); } return escapeValues; } private class FormHttpInputMessage implements HttpInputMessage { private HttpServletRequest request; public FormHttpInputMessage(HttpServletRequest request) { this.request = request; } @Override public HttpHeaders getHeaders() { HttpHeaders headers = new HttpHeaders(); Enumeration<String> headerNames = request.getHeaderNames(); while (headerNames.hasMoreElements()) { String name = headerNames.nextElement(); String headerValue = request.getHeader(name); headers.add(headerNames.nextElement(), headerValue); } return headers; } @Override public InputStream getBody() throws IOException { return request.getInputStream(); } } }
3.web.xml配置web
<filter> <filter-name>httpFormFilter</filter-name> <filter-class>com.jing.springboot.test.HttpContentFormFilter</filter-class> </filter> <filter-mapping> <filter-name>httpFormFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
对上传的数据不做html过滤,对返回的数据呈如今页面上使用html标签过滤,建议采用,写一个专门的公用类便可spring
//html标签转义成自定义字符 function html2Escape(sHtml) { return sHtml.replace(/[<>&"]/g,function(c){ return {'<':'<','>':'>','&':'&','"':'"'}[c]; }); }
xss问题属于被动式攻击,通常很容易被忽略,在项目的安全检测中遇到此问题,在此做下笔记方便之后查阅数据库