question-sky

xss跨站脚本攻击问题最主要是呈现在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()]);
    }
}
  1. 创建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;

/**

  • 参数拦截过滤器
  • @author jtj
  • */
    public class HttpContentFormFilter extends OncePerRequestFilter {

    private List supportMediaTypes = new ArrayList();

    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 map) {
    Set<Entry<String, List>> mapEntrySet = map.entrySet();
    for (Entry<String, List> mapEntry : mapEntrySet) {
    mapEntry.setValue(escapeHtml(mapEntry.getValue()));
    }
    }

    private List escapeHtml(List values) {
    List escapeValues = new ArrayList(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();
    }

    }

}

```

  1. web.xml配置
    ```xml

    httpFormFilter
    com.jing.springboot.test.HttpContentFormFilter


httpFormFilter
/*

```

前端屏蔽

对上传的数据不作html过滤,对返回的数据呈现在页面上使用html标签过滤,建议采用,写一个专门的公用类即可

//html标签转义成自定义字符
function html2Escape(sHtml) { 
    return sHtml.replace(/[<>&"]/g,function(c){
        return {'<':'&lt;','>':'&gt;','&':'&amp;','"':'&quot;'}[c];
    }); 
}

总结

xss问题属于被动式攻击,一般很容易被忽略,在项目的安全检测中遇到此问题,在此作下笔记方便以后查阅

相关文章: