XSS攻击,使用Filter转义

Filter转义后的特殊字符,如果需要直接使用可以通过 XssFilter.XssHttpServletRequestWrapper.unescapeHtml(value)还原,入库和传给前端用转义后的字符串,前端自行还原

package com.suyun.common.filters;

import com.suyun.common.utils.Encodes;
import org.apache.commons.lang.StringEscapeUtils;
import org.apache.commons.lang3.StringUtils;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Description:
 * <p>
 * URL和参数转义Filter
 * </p>
 *
 * @Author: leo.xiong
 * @CreateDate: 2022/9/8 9:04
 * @Email: leo.xiong@suyun360.com
 * @Since:
 */
@WebFilter(urlPatterns = "/*", filterName = "xssFilter")
public class XssFilter implements Filter {
    private static final Pattern ILLEGAL_ONE_PATTERN = Pattern.compile("--><script>.*</script><!--");

    private static final Pattern ILLEGAL_TWO_PATTERN = Pattern.compile("<script>.*</script>");

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

    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        if (request instanceof HttpServletRequest) {
            //参数转义
            HttpServletRequest wrapper = new XssHttpServletRequestWrapper((HttpServletRequest) request);
            String url = wrapper.getRequestURL().toString();
            if (StringUtils.isEmpty(url)) {
                chain.doFilter(wrapper, response);
                return;
            }
            String newUrl = replace(url, ILLEGAL_ONE_PATTERN);
            if (url.equals(newUrl)) {
                chain.doFilter(wrapper, response);
                return;
            }
            //URL转义
            wrapper.getRequestDispatcher(newUrl).forward(wrapper, response);
            return;
        }
        chain.doFilter(request, response);
    }

    private String replace(String url, Pattern pattern) {
        Matcher match = pattern.matcher(url);
        if (match.find()) {
            String value = match.group(0);
            try {
                url = url.replace(value, URLEncoder.encode(value, StandardCharsets.UTF_8.name()));
                replace(url, pattern);
            } catch (UnsupportedEncodingException e) {

            }
        } else {
            return url;
        }
        return replace(url, ILLEGAL_TWO_PATTERN);
    }

    @Override
    public void destroy() {

    }

    /**
     * 防范XSS攻击
     */
    public static class XssHttpServletRequestWrapper extends HttpServletRequestWrapper {
        public XssHttpServletRequestWrapper(HttpServletRequest servletRequest) {
            super(servletRequest);
        }

        /**
         * 使用参数时调用的方法,会把参数转义
         *
         * @param parameter
         * @return
         */
        @Override
        public String[] getParameterValues(String parameter) {
            String[] values = super.getParameterValues(parameter);
            if (values == null) {
                return null;
            }
            int count = values.length;
            String[] encodedValues = new String[count];
            for (int i = 0; i < count; i++) {
                encodedValues[i] = Encodes.escapeHtml(values[i]);
            }
            return encodedValues;
        }

        /**
         * 使用参数时调用的方法,会把参数转义
         *
         * @param parameter
         * @return
         */
        @Override
        public String getParameter(String parameter) {
            String value = super.getParameter(parameter);
            if (value == null) {
                return null;
            }
            return Encodes.escapeHtml(value);
        }

        /**
         * 使用参数时调用的方法,会把参数转义
         *
         * @param name
         * @return
         */
        @Override
        public String getHeader(String name) {
            String value = super.getHeader(name);
            if (value == null) {
                return null;
            }
            return Encodes.escapeHtml(value);
        }

        /**
         * 还原
         * @param value
         * @return
         */
        public static final String unescapeHtml(String value) {
            return StringEscapeUtils.unescapeXml(Encodes.unescapeHtml(value));
        }
    }
}
package com.suyun.common.utils;

import org.apache.commons.codec.DecoderException;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.binary.Hex;
import org.apache.commons.lang3.StringEscapeUtils;

import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;

/**
 * Description:
 * <p>
 * * 封装各种格式的编码解码工具类.
 * * 1.Commons-Codec的 hex/base64 编码
 * * 2.自制的base62 编码
 * * 3.Commons-Lang的xml/html escape
 * * 4.JDK提供的URLEncoder
 * </p>
 *
 * @Author: leo.xiong
 * @CreateDate: 2022/9/9 15:14
 * @Email: leo.xiong@suyun360.com
 * @Since:
 */
public class Encodes {

    private static final String DEFAULT_URL_ENCODING = "UTF-8";
    private static final char[] BASE62 = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz".toCharArray();

    /**
     * Hex编码.
     */
    public static String encodeHex(byte[] input) {
        return new String(Hex.encodeHex(input));
    }

    /**
     * Hex解码.
     */
    public static byte[] decodeHex(String input) {
        try {
            return Hex.decodeHex(input.toCharArray());
        } catch (DecoderException e) {
            throw Exceptions.unchecked(e);
        }
    }

    /**
     * Base64编码.
     */
    public static String encodeBase64(byte[] input) {
        return new String(Base64.encodeBase64(input));
    }

    /**
     * Base64编码.
     */
    public static String encodeBase64(String input) {
        try {
            return new String(Base64.encodeBase64(input.getBytes(DEFAULT_URL_ENCODING)));
        } catch (UnsupportedEncodingException e) {
            return "";
        }
    }

//	/**
//	 * Base64编码, URL安全(将Base64中的URL非法字符'+'和'/'转为'-'和'_', 见RFC3548).
//	 */
//	public static String encodeUrlSafeBase64(byte[] input) {
//		return Base64.encodeBase64URLSafe(input);
//	}

    /**
     * Base64解码.
     */
    public static byte[] decodeBase64(String input) {
        return Base64.decodeBase64(input.getBytes());
    }

    /**
     * Base64解码.
     */
    public static String decodeBase64String(String input) {
        try {
            return new String(Base64.decodeBase64(input.getBytes()), DEFAULT_URL_ENCODING);
        } catch (UnsupportedEncodingException e) {
            return "";
        }
    }

    /**
     * Base62编码。
     */
    public static String encodeBase62(byte[] input) {
        char[] chars = new char[input.length];
        for (int i = 0; i < input.length; i++) {
            chars[i] = BASE62[((input[i] & 0xFF) % BASE62.length)];
        }
        return new String(chars);
    }

    /**
     * Html 转码.
     */
    public static String escapeHtml(String html) {
        return StringEscapeUtils.escapeHtml4(html);
    }

    /**
     * Html 解码.
     */
    public static String unescapeHtml(String htmlEscaped) {
        return StringEscapeUtils.unescapeHtml4(htmlEscaped);
    }

    /**
     * Xml 转码.
     */
    public static String escapeXml(String xml) {
        return StringEscapeUtils.escapeXml10(xml);
    }

    /**
     * Xml 解码.
     */
    public static String unescapeXml(String xmlEscaped) {
        return StringEscapeUtils.unescapeXml(xmlEscaped);
    }

    /**
     * URL 编码, Encode默认为UTF-8.
     */
    public static String urlEncode(String part) {
        try {
            return URLEncoder.encode(part, DEFAULT_URL_ENCODING);
        } catch (UnsupportedEncodingException e) {
            throw Exceptions.unchecked(e);
        }
    }

    /**
     * URL 解码, Encode默认为UTF-8.
     */
    public static String urlDecode(String part) {

        try {
            return URLDecoder.decode(part, DEFAULT_URL_ENCODING);
        } catch (UnsupportedEncodingException e) {
            throw Exceptions.unchecked(e);
        }
    }
}
public static void main(String[] args) throws UnsupportedEncodingException {
        String value = "{\"张三\":\"12\",\"李四\":\"<script>alert(1)</script>\"}";
        //转义
        String escape = StringEscapeUtils.escapeHtml(value);
        System.out.println(escape);
        //还原
        String unescape = XssFilter.XssHttpServletRequestWrapper.unescapeHtml(escape);
        System.out.println(unescape);
    }

在这里插入图片描述


版权声明:本文为xionglangs原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。