前言

可能很多同学在学习过程中自己做项目,很容易忽略XSS攻击。网上不少博客的自定义全局拦截器来实现XSS过滤,其实不需要这么麻烦,SpringBoot留有不少钩子(扩展点),据此我们可以巧妙地实现全局的XSS过滤。

防止XSS攻击,一般有两种做法:

1、转义

Spring有提供工具类HtmlUtils来实现转义。个人比较喜欢这种方式,所以下面代码均采用转义处理。

2、过滤敏感标签(将敏感标签去除)

jsoup实现了非常强大的clean敏感标签的功能,但是我对jsoup了解并不多。

接下来的问题,就是:不适用拦截器,在哪切入,对参数进行转义处理呢?答案是:SpringMVC 进行参数绑定时!关于源码级别的理解和说明,我就不说了,因为能力有限。详情可参考:https://www.cnblogs.com/yourbatman/p/11218694.html

下面我会提供3种配置方式!个人比较推荐 方式3,因为WebMvcConfigurer本身就是为开发者定制的配置MVC的非常强大的扩展接口。

方式1

import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.util.HtmlUtils;

import java.beans.PropertyEditorSupport;

/**
 * String->String的类型转换器
 *
 * @author passerbyYSQ
 * @create 2021-02-22 17:26
 */
@Component
public class EscapeStringEditor extends PropertyEditorSupport {
    
    @Override
    public String getAsText() {
        Object value = getValue();
        return value != null ? value.toString() : "";
    }

    @Override
    public void setAsText(String text) throws IllegalArgumentException {
        String escapedText = null;
        if (!StringUtils.isEmpty(text)) {
            escapedText = HtmlUtils.htmlEscape(text, "UTF-8");
        }
        setValue(escapedText);
    }
}
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.support.ConfigurableWebBindingInitializer;

/**
 * @author passerbyYSQ
 * @create 2021-02-22 17:20
 */
@Component
public class MyWebBindingInitializer extends ConfigurableWebBindingInitializer {

    @Autowired
    private EscapeStringEditor escapeStringEditor;

    @Override
    public void initBinder(WebDataBinder binder) {
        super.initBinder(binder); // 不能少!!!
        // 注册自定义的类型转换器
        binder.registerCustomEditor(String.class, escapeStringEditor);
    }

}

方式2

public class BaseController {

    @Autowired
    private EscapeStringEditor escapeStringEditor;

    @InitBinder
    public void initBinder(ServletRequestDataBinder binder) {
        binder.registerCustomEditor(String.class, escapeStringEditor);
    }

需要XSS防护的Controller的需要继承该BaseController

方式3

import org.springframework.core.convert.converter.Converter;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.util.HtmlUtils;

/**
 * @author passerbyYSQ
 * @create 2021-02-22 23:15
 */
@Component
public class EscapeStringConverter implements Converter<String, String> {

    @Override
    public String convert(String s) {
        return StringUtils.isEmpty(s) ? s : HtmlUtils.htmlEscape(s);
    }

}
package net.ysq.webchat.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.format.FormatterRegistry;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

/**
 * @author passerbyYSQ
 * @create 2021-01-29 14:41
 */
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

    @Autowired
    private LoginInterceptor loginInterceptor;

    @Autowired
    private EscapeStringConverter escapeStringConverter;

    /**
     * 在参数绑定时,自定义String->String的转换器,
     * 在转换逻辑中对参数值进行转义,从而达到防XSS的效果
     *
     * @param registry
     */
    @Override
    public void addFormatters(FormatterRegistry registry) {
        registry.addConverter(escapeStringConverter);
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(loginInterceptor)
                .addPathPatterns("/**")
                // 路径不包括contextPath部分
                .excludePathPatterns("/user/login", "/user/logout", "/index/test1");
    }

    /**
     * 前后端分离需要解决跨域问题
     *
     * @param registry
     */
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
                .allowedOrigins("*")
                .allowedMethods("GET", "POST", "PUT", "OPTIONS", "DELETE", "PATCH")
                .allowCredentials(true).maxAge(3600);
    }

}

 

本文地址:https://blog.csdn.net/qq_43290318/article/details/113963435