springMVC使用Antisamy防御XSS配置(包括请求黑白名单)

AntiSamy是OWASP的一个开源项目,通过对用户输入的 HTML / CSS / JavaScript 等内容进行检验和清理,确保输入符合应用规范。AntiSamy被广泛应用于Web服务对存储型和反射型XSS的防御中。

1、maven依赖

	<dependency>
		<groupId>org.owasp.antisamy</groupId>
		<artifactId>antisamy</artifactId>
		<version>1.6.2</version>
	</dependency>

2、选择策略文件

AntiSamy对“恶意代码”的过滤依赖于策略文件,详细的可以查下资料哦。
在AntiSamy的jar包中,包含了几个常用的策略文件,我就以"antisamy-ebay.xml"为例。
在这里插入图片描述

3、XSS过滤器

package com.security.filter;

import com.security.wrapper.XssRequestWrapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.PathMatcher;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

public class XssFilter implements Filter {

    private FilterConfig config;

    private final PathMatcher pathMatcher = new AntPathMatcher();
    private static final Logger LOGGER = LoggerFactory.getLogger(XssFilter.class);
    private final String[] NULL_STRING_ARRAY = new String[0];
    private final String URL_SPLIT_PATTERN = "[, ;\r\n]";//逗号  空格 分号  换行

    /**
     * 白名单
     */
    private String[] whiteListURLs = null;

    /**
     * 黑名单
     */
    private String[] blackListURLs = null;


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

    public void init() throws ServletException {

    }

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

        HttpServletRequest httpRequest = (HttpServletRequest) request;
        HttpServletResponse httpResponse = (HttpServletResponse) response;

        String currentURL = httpRequest.getServletPath();

        if (isWhiteURL(currentURL)) {
            doFilter(httpRequest, httpResponse, chain);
            return;
        }
        LOGGER.info(" 拦截请求,处理XSS过滤 当前 url : [{}]", currentURL);
        // 拦截请求,处理XSS过滤
        chain.doFilter(new XssRequestWrapper((HttpServletRequest)request), response);
        return;
    }
    /**
     * 子类覆盖
     *
     * @param request
     * @param response
     * @param chain
     * @throws IOException
     * @throws ServletException
     */
    public void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        chain.doFilter(request, response);
    }
    @Override
    public void destroy() {
        this.config = null;
    }

    private boolean isWhiteURL(String currentURL) {
        for (String whiteURL : whiteListURLs) {
            if (pathMatcher.match(whiteURL, currentURL)) {
                return true;
            }
        }
        return false;
    }

    private boolean isBlackURL(String currentURL) {
        for (String blackURL : blackListURLs) {
            if (pathMatcher.match(blackURL, currentURL)) {
                return true;
            }
        }
        return false;
    }
    private void initConfig() {
        String whiteListURLStr = this.config.getInitParameter("whiteListURL");
        whiteListURLs = strToArray(whiteListURLStr);

        String blackListURLStr = this.config.getInitParameter("blackListURL");
        blackListURLs = strToArray(blackListURLStr);

    }

    private String[] strToArray(String urlStr) {
        if (urlStr == null) {
            return NULL_STRING_ARRAY;
        }
        String[] urlArray = urlStr.split(URL_SPLIT_PATTERN);

        List<String> urlList = new ArrayList<String>();

        for (String url : urlArray) {
            url = url.trim();
            if (url.length() == 0) {
                continue;
            }

            urlList.add(url);
        }

        return urlList.toArray(NULL_STRING_ARRAY);
    }

    public FilterConfig getConfig() {
        return config;
    }
}

4、自定义的XSS请求装饰器

装饰器模式加强对request的处理,基于AntiSamy进行XSS防御

package com.security.wrapper;

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.text.StringEscapeUtils;
import org.owasp.validator.html.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.Map;
import java.util.Objects;

/**
 * @description 装饰器模式加强对request的处理,基于AntiSamy进行XSS防御
 */
public class XssRequestWrapper extends HttpServletRequestWrapper {

    private static final Logger LOGGER = LoggerFactory.getLogger(XssRequestWrapper.class);
    private static Policy policy = null;

    static {
        try {
            // 获取策略文件路径,策略文件需要放到项目的classpath下
            String antiSamyPath = Objects
                    .requireNonNull(XssRequestWrapper.class.getClassLoader().getResource("antisamy/antisamy-ebay.xml")).getFile();
            LOGGER.info(antiSamyPath);
            // 获取的文件路径中有空格时,空格会被替换为%20,在new一个File对象时会出现找不到路径的错误
            // 对路径进行解码以解决该问题
            antiSamyPath = URLDecoder.decode(antiSamyPath, "utf-8");
            LOGGER.info(antiSamyPath);
            // 指定策略文件
            policy = Policy.getInstance(antiSamyPath);
        } catch (UnsupportedEncodingException | PolicyException e) {
            e.printStackTrace();
        }
    }

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

    /**
     * 过滤请求头
     *
     * @param name 参数名
     * @return 参数值
     */
    @Override
    public String getHeader(String name) {
        String header = super.getHeader(name);
        // 如果Header为空,则直接返回,否则进行清洗
        return StringUtils.isBlank(header) ? header : xssClean(header);
    }

    /**
     * 过滤请求参数
     *
     * @param name 参数名
     * @return 参数值
     */
    @Override
    public String getParameter(String name) {
        String parameter = super.getParameter(name);
        // 如果Parameter为空,则直接返回,否则进行清洗
        return StringUtils.isBlank(parameter) ? parameter : xssClean(parameter);
    }

    /**
     * 过滤请求参数(一个参数可以有多个值)
     *
     * @param name 参数名
     * @return 参数值数组
     */
    @Override
    public String[] getParameterValues(String name) {
        String[] parameterValues = super.getParameterValues(name);
        if (parameterValues != null) {
            int length = parameterValues.length;
            String[] newParameterValues = new String[length];
            for (int i = 0; i < length; i++) {
                LOGGER.info("AntiSamy清理之前的参数值:" + parameterValues[i]);
                // 清洗参数
                newParameterValues[i] = xssClean(parameterValues[i]);
                LOGGER.info("AntiSamy清理之后的参数值:" + newParameterValues[i]);
            }
            return newParameterValues;
        }
        return super.getParameterValues(name);
    }

    @Override
    public Map<String, String[]> getParameterMap() {
        Map<String, String[]> requestMap = super.getParameterMap();
        requestMap.forEach((key, value) -> {
            for (int i = 0; i < value.length; i++) {
                LOGGER.info(value[i]);
                value[i] = xssClean(value[i]);
                LOGGER.info(value[i]);
            }
        });
        return requestMap;
    }

    /**
     * 使用AntiSamy清洗数据
     *
     * @param value 需要清洗的数据
     * @return 清洗后的数据
     */
    private String xssClean(String value) {
        try {
            AntiSamy antiSamy = new AntiSamy();
            // 使用AntiSamy清洗数据
            final CleanResults cleanResults = antiSamy.scan(value, policy);
            // 获得安全的HTML输出
            value = cleanResults.getCleanHTML();
            // 对转义的HTML特殊字符(<、>、"等)进行反转义,因为AntiSamy调用scan方法时会将特殊字符转义
            return StringEscapeUtils.unescapeHtml4(value);
        } catch (ScanException | PolicyException e) {
            e.printStackTrace();
        }
        return value;
    }

    /**
     * 通过修改Json序列化的方式来完成Json格式的XSS过滤
     */
    public static class XssStringJsonSerializer extends JsonSerializer<String> {

        @Override
        public Class<String> handledType() {
            return String.class;
        }

        @Override
        public void serialize(String value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
            if (!StringUtils.isBlank(value)) {
                try {
                    AntiSamy antiSamy = new AntiSamy();
                    final CleanResults cleanResults = antiSamy.scan(value, XssRequestWrapper.policy);
                    gen.writeString(StringEscapeUtils.unescapeHtml4(cleanResults.getCleanHTML()));
                } catch (ScanException | PolicyException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

5、web.xml

黑白名单说明:

  • 黑名单可以不用设置
  • 白名单是不会被拦截的,如果某些请求不需要被拦截,那么可以放入白名单内。每条链接需要用逗号 空格 分号 换行 隔开。
<!-- XSS -->
    <filter>
        <filter-name>XSSFilter</filter-name>
        <filter-class>com.security.filter.XssFilter</filter-class>
        <init-param>
            <param-name>blackListURL</param-name>
            <param-value>
                /blackListURL
            </param-value>
        </init-param>
        <init-param>
            <param-name>whiteListURL</param-name>
            <param-value>
                /whiteListURL/**
            </param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>XSSFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Abdulaziz02

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值