java

SpringMVC过滤字符防御xss攻击

2021-06-18  本文已影响0人  星钻首席小管家

1.参数重写类ParameterRequestWrapper

package com.yxtech.business.filter;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.yxtech.utils.StringUtils;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.IOException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
/**
 * 参数重写类
 * @author sp.
 * <br/>date: 2021-06-18.
 */
public class ParameterRequestWrapper {
    /**
     * 过滤JSON数据请求参数
     * @param request
     * @param response
     */
    @SuppressWarnings("unchecked")
    public ServletRequest parameterRequest(HttpServletRequest request, ServletResponse response) throws IOException {

        RepeatedlyRequestWrapper requestWrapper = new RepeatedlyRequestWrapper(request, response);

        new ParameterRequestWrapper().parameterGetRequest(request,requestWrapper);
        // 读取请求内容
        BufferedReader br;
        br = requestWrapper.getReader();
        String line = null;
        StringBuilder sb = new StringBuilder();
        while ((line = br.readLine()) != null) {
            sb.append(line);
        }

        JSONObject jsonObject = JSONObject.parseObject(sb.toString());
        Map<String, Object> map = new HashMap<String, Object>();
        map = JSON.toJavaObject(jsonObject, Map.class);

        if(null != map) {
            for (Map.Entry<String, Object> entry : map.entrySet()) {
                Class<?> aClass = entry.getValue().getClass();
                String name = aClass.getName();
                if(StringUtils.isNotEmpty(name) && "java.lang.String".equalsIgnoreCase(name)){
                    // 重写参数
                    map.put(entry.getKey(), xssEncode(entry.getValue().toString().trim()));
                }
            }
            // 把参数转换之后放到我们的body里面
            String json = JSON.toJSONString(map);
            requestWrapper.setBody(json.getBytes("UTF-8"));
        }
        return requestWrapper;
    }
    /**
     * 过滤URL请求参数
     * @param request
     * @param requestWrapper
     */
    public void  parameterGetRequest(HttpServletRequest request, RepeatedlyRequestWrapper requestWrapper){

        Map<String, String[]> parameterMap = request.getParameterMap();
        Map<String, String[]> parames = new HashMap<String, String[]>();
        if(null != parameterMap) {
            Set<String> set =parameterMap.keySet();
            Iterator<String> it=set.iterator();
            while(it.hasNext()){
                String key= (String) it.next();
                String[] values = parameterMap.get(key);
                //values[0] = values[0].trim();
                values[0] = xssEncode(values[0].trim());
                parames.put(key, values);
            }
            requestWrapper.setParams(parames);
        }
    }

    /**
     * 将容易引起xss漏洞的半角字符直接替换成全角字符
     *
     * @param s
     * @return
     */
    private static String xssEncode(String s) {
        if (s == null || s.isEmpty()) {
            return s;
        }
        StringBuilder sb = new StringBuilder(s.length() + 16);
        for (int i = 0; i < s.length(); i++) {
            char c = s.charAt(i);
            switch (c) {
                case '>':
                    sb.append('>');// 全角大于号
                    break;
                case '<':
                    sb.append('<');// 全角小于号
                    break;
                case '\'':
                    sb.append('‘');// 全角单引号
                    break;
                case '\"':
                    sb.append('“');// 全角双引号
                    break;
                case '&':
                    sb.append('&');// 全角
                    break;
                case '\\':
                    sb.append('\');// 全角斜线
                    break;
                case '#':
                    sb.append('#');// 全角井号
                    break;
//                case '%':    // < 字符的 URL 编码形式表示的 ASCII 字符(十六进制格式) 是: %3c
//                    processUrlEncoder(sb, s, i);
//                    break;
                default:
                    sb.append(c);
                    break;
            }
        }
        return sb.toString();
    }

    public static void processUrlEncoder(StringBuilder sb, String s, int index){
        if(s.length() >= index + 2){
            if(s.charAt(index+1) == '3' && (s.charAt(index+2) == 'c' || s.charAt(index+2) == 'C')){    // %3c, %3C
                sb.append('<');
                return;
            }
            if(s.charAt(index+1) == '6' && s.charAt(index+2) == '0'){    // %3c (0x3c=60)
                sb.append('<');
                return;
            }
            if(s.charAt(index+1) == '3' && (s.charAt(index+2) == 'e' || s.charAt(index+2) == 'E')){    // %3e, %3E
                sb.append('>');
                return;
            }
            if(s.charAt(index+1) == '6' && s.charAt(index+2) == '2'){    // %3e (0x3e=62)
                sb.append('>');
                return;
            }
        }
        sb.append(s.charAt(index));
    }

}



2.读取body参数类RepeatedlyRequestWrapper

package com.yxtech.business.filter;


import javax.servlet.ServletInputStream;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.Map;
/**
 * 读取body参数
 * @author sp.
 * <br/>date: 2021-06-18.
 */
public class RepeatedlyRequestWrapper extends HttpServletRequestWrapper {

    private Map<String , String[]> params = new HashMap<String, String[]>();
    private  byte[] body;

    public RepeatedlyRequestWrapper(HttpServletRequest request, ServletResponse response) throws IOException {
        super(request);
        request.setCharacterEncoding("UTF-8");
        response.setCharacterEncoding("UTF-8");

        body = HttpHelper.getBodyString(request).getBytes("UTF-8");
    }

    @Override
    public BufferedReader getReader() throws IOException {
        return new BufferedReader(new InputStreamReader(getInputStream()));
    }

    @Override
    public ServletInputStream getInputStream() throws IOException {

        final ByteArrayInputStream bais = new ByteArrayInputStream(body);

        return new ServletInputStream() {

            @Override
            public int read() throws IOException {
                return bais.read();
            }
        };

    }

    //重写getParameter 参数从当前类中的map获取
    @Override
    public String[] getParameterValues(String name) {
        return params.get(name);
    }

    public byte[] getBody() {
        return body;
    }

    public void setBody(byte[] body) {
        this.body = body;
    }

    public void setParams(Map<String, String[]> params) {
        this.params = params;
    }


}

3.将字节数组转换为字符串HttpHelper

package com.yxtech.business.filter;

import javax.servlet.ServletRequest;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.Charset;

/**
 * 用于将字节数组转换为字符串
 */
public class HttpHelper {
    /**
     * 获取请求Body
     *
     * @param request
     * @return
     */
    public static String getBodyString(ServletRequest request) {
        StringBuilder sb = new StringBuilder();
        InputStream inputStream = null;
        BufferedReader reader = null;
        try {
            inputStream = request.getInputStream();
            reader = new BufferedReader(new InputStreamReader(inputStream, Charset.forName("UTF-8")));
            String line = "";
            while ((line = reader.readLine()) != null) {
                sb.append(line);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (reader != null) {
                try {
                    reader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return sb.toString();
    }
}

4.过滤器RepeatableFilter

package com.yxtech.business.filter;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;

/**
 * Repeatable 过滤器
 * @author sp.
 * <br/>date: 2021-06-18.
 */
public class RepeatableFilter implements Filter {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {

        String contentType = request.getContentType();
        ServletRequest requestWrapper = null;

        // 如果上传的是文件(multipart/form-data),就不走过滤
        if (request instanceof HttpServletRequest) {
            if (null != contentType && contentType.contains("multipart/form-data")) {
                return;
            }else {
                requestWrapper = new ParameterRequestWrapper().parameterRequest((HttpServletRequest) request, response);
            }
        }


        if (null == requestWrapper) {
            chain.doFilter(request, response);
        } else {
            chain.doFilter(requestWrapper, response);
        }
    }

    @Override
    public void destroy() {

    }
}

5.web.xml配置过滤器

<!-- xss字符参数过滤器  -->
    <filter>
        <filter-name>ParamsFilter</filter-name>
        <filter-class>com.yxtech.business.filter.RepeatableFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>ParamsFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
上一篇 下一篇

猜你喜欢

热点阅读