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問題屬於被動式攻擊,通常很容易被忽略,在項目的安全檢測中遇到此問題,在此做下筆記方便之後查閱數據庫