JAVA后端防止XSS攻击(SQL注入、HTML、SCRIPT)基础方法

今天在工作中发现自己开发的系统存在XSS漏洞,特写此文章记录解决方案,话不多说截止开车。

防止XSS攻击我们需要做的除了在硬件层面(防火墙、IP白名单)验证外,后端代码也需要做相应的处理,最简单直接的方式添加Filter过滤器。

/**
 * @Project : codeyi-web
 * @Package Name : com.codeyi.common.xss
 * @Company 
 * @Author Codeyi on 2019-9-11.
 * @Creation Date: 2019年09月11日 13:32
 * @Description :描述
 */
public class RequestXssFilter implements Filter {
    FilterConfig filterConfig = null;

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        this.filterConfig = filterConfig;
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        filterChain.doFilter(new XssHttpServletRequestWrapper(
                    (HttpServletRequest) servletRequest), servletResponse);
        }

        @Override
        public void destroy () {
            this.filterConfig = null;
        }
    }

实际过滤类

/**
 * @Project : codeyi-web
 * @Package Name : com.codeyi.common.xss
 * @Company 
 * @Author Codeyi on 2019-9-11.
 * @Creation Date: 2019年09月11日 13:34
 * @Description :描述
 */
public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper {

    //白名单数组
    private static final String[] WHITE_LIST = {"content"};
    // 定义script的正则表达式
    private static final String REGEX_SCRIPT = "]*?>[\\s\\S]*?<\\/script>";
    // 定义style的正则表达式
    private static final String REGEX_STYLE = "]*?>[\\s\\S]*?<\\/style>";
    // 定义HTML标签的正则表达式
    private static final String REGEX_HTML = "<[^>]+>";
    // 定义空格回车换行符
    private static final String REGEX_SPACE = "\\s*|\t|\r|\n";
    //定义所有w标签
    private static final String REGEX_W = "]*?>[\\s\\S]*?<\\/w[^>]*?>";
    //定义SQL注入
    private static String reg = "(\\b(select|update|union|and|or|delete|insert|trancate|char|into|substr|ascii|declare|exec|count|master|into|drop|execute)\\b)";

    public XssHttpServletRequestWrapper(HttpServletRequest request) {
        super(request);
    }

    @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++) {
            //白名单放行的只有HTML标签,SQL标签还是要验证
            if (isWhitelist(parameter)) {
                if (sqlValidate(values[i])) {
                    encodedValues[i] = values[i];
                }
                encodedValues[i] = null;
            }
            encodedValues[i] = removeHtml(values[i]);
        }

        return encodedValues;

    }

    @Override
    public String getParameter(String parameter) {
        String value = super.getParameter(parameter);
        if (value == null) {
            return null;
        }
        //白名单放行的只有HTML标签,SQL标签还是要验证
        if (isWhitelist(parameter)) {
            if (sqlValidate(value)) {
                return value;
            }
            return null;
        }
        return removeHtml(value);
    }

    @Override
    public String getHeader(String name) {
        String value = super.getHeader(name);
        if (value == null) {
            return null;
        }

        if (isWhitelist(name)) {
            if (sqlValidate(value)) {
                return value;
            }
            return null;
        }
        return removeHtml(value);
    }


    //\\b  表示 限定单词边界  比如  select 不通过   1select则是可以的
    private static Pattern sqlPattern = Pattern.compile(reg, Pattern.CASE_INSENSITIVE);

    /**
     * SQL注入过滤器
     * @param str
     * @return
     */
    private static boolean sqlValidate(String str) {
        if (sqlPattern.matcher(str).find()) {
            System.out.println("未能通过过滤器:str=" + str);
            return false;
        }
        return true;
    }

    /**
     * 是否白名单,白名单的放行
     *
     * @param paramName
     * @return
     */
    private static boolean isWhitelist(String paramName) {
        String lowerParam = paramName.toLowerCase();
        String name = Arrays.stream(WHITE_LIST).filter(y -> y.toLowerCase().equals(lowerParam)).findAny().orElse(null);
        return name != null;
    }

    /**
     * 移除HTML标签
     * @param htmlStr
     * @return
     */
    private static String removeHtml(String htmlStr){
        Pattern p_w = Pattern.compile(REGEX_W, Pattern.CASE_INSENSITIVE);
        Matcher m_w = p_w.matcher(htmlStr);
        htmlStr = m_w.replaceAll(""); // 过滤script标签


        Pattern p_script = Pattern.compile(REGEX_SCRIPT, Pattern.CASE_INSENSITIVE);
        Matcher m_script = p_script.matcher(htmlStr);
        htmlStr = m_script.replaceAll(""); // 过滤script标签


        Pattern p_style = Pattern.compile(REGEX_STYLE, Pattern.CASE_INSENSITIVE);
        Matcher m_style = p_style.matcher(htmlStr);
        htmlStr = m_style.replaceAll(""); // 过滤style标签


        Pattern p_html = Pattern.compile(REGEX_HTML, Pattern.CASE_INSENSITIVE);
        Matcher m_html = p_html.matcher(htmlStr);
        htmlStr = m_html.replaceAll(""); // 过滤html标签


        Pattern p_space = Pattern.compile(REGEX_SPACE, Pattern.CASE_INSENSITIVE);
        Matcher m_space = p_space.matcher(htmlStr);
        htmlStr = m_space.replaceAll(""); // 过滤空格回车标签


        htmlStr = htmlStr.replaceAll(" ", ""); //过滤
        return htmlStr.trim(); // 返回文本字符串
    }
}

Web.xml中添加过滤器


    XssEscape
    cn.pinming.common.xss.RequestXssFilter

如发现存在不正确的地方还请大家多多提点。

2019年12月6日补充不区分大小写正则验证

// 定义script的正则表达式

private static final String REGEX_SCRIPT = “<((?i)script)[^>]?>[\s\S]?<\/((?i)script)>”;

// 定义style的正则表达式

private static final String REGEX_STYLE = “<((?i)style)[^>]?>[\s\S]?<\/((?i)style)>”;

你可能感兴趣的:(Java,XSS)