XSS(Cross-site scripting)跨站脚本攻击是一种常见的Web攻击,攻击者往Web页面里插入恶意的脚本代码,使得用户在浏览页面或提交表单的时候,执行恶意脚本,从而达到攻击目的。为了防止XSS攻击,Springboot提供了多种方式去过滤用户输入的数据,保证系统的安全稳定。
一、 Springboot过滤器
1、过滤器介绍
过滤器是用于拦截请求与响应,可以用于URL、Servlet、JSP、静态文件等资源的拦截。Springboot的过滤器实现了javax.servlet.Filter接口,提供了过滤器的常见功能,是一种非常常用的过滤方式。
2、过滤器示例
在Springboot中,过滤器是通过@Bean把过滤器加入到过滤器链中的,可以通过注解@WebFilter实现。以下示例会构建一个过滤器,对所有请求进行XSS过滤:
@WebFilter(urlPatterns = "/*", filterName = "xssFilter")
public class XssFilter implements Filter {
  @Override
  public void init(FilterConfig filterConfig) throws ServletException { }
  
  @Override
  public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
    chain.doFilter(new XssHttpServletRequestWrapper((HttpServletRequest) request), response);
  }
  
  @Override
  public void destroy() { }
}
在过滤器中,我们通过构建XssHttpServletRequestWrapper来过滤请求。这个Wrapper里重写了getParameter方法,对请求中的参数进行了过滤。XssHttpServletRequestWrapper的实现如下:
public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper {
  public XssHttpServletRequestWrapper(HttpServletRequest request) {
    super(request);
  }
  @Override
  public String getParameter(String name) {
    String value = super.getParameter(name);
    if (value == null) {
        return null;
    }
    return HtmlUtils.htmlEscape(value);
  }
}
在getParameter方法中,我们使用了org.springframework.web.util.HtmlUtils的htmlEscape方法对请求参数进行了过滤,把所有的HTML标签都转换成了实体,防止了XSS攻击。
二、 Springboot注解
1、注解介绍
Springboot通过注解的方式来处理XSS过滤,可以直接在Controller层的方法参数上使用@Validated注解,在参数中使用@NotBlank或其他JSR303注解,并配合@RequestBody、@RequestParam可以过滤掉所有HTML标签,非常方便易用。
2、注解示例
以下例子演示了如何使用@RequestBody和@RequestParam结合JSR303注解来实现对控制器请求参数的过滤:
@RestController
public class UserController {
    @PostMapping("/user")
    public void addUser(@Validated @RequestBody UserDTO userDTO) {
        // Todo
    }
    
    @GetMapping("/users")
    public List getUsers(@RequestParam @NotBlank String name) {
        // Todo
    }
}
在addUser方法中,我们使用了@Validated注解,同时在UserDTO的name字段上使用了@NotBlank注解,来实现对请求参数的过滤。在getUsers方法中,我们也显示的用@RequestParam声明了参数name,并使用了@NotBlank注解来过滤请求。
三、Springboot过滤器加上自定义注解
1、自定义注解介绍
自定义注解可以让我们更好的管理XSS过滤,使得代码更加可读、易用、易维护,可以在Springboot过滤器的基础上添加自定义注解,来实现更加灵活的过滤。
2、自定义注解示例
以下代码演示了如何在过滤器中添加自定义注解:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface XssSecurity {
     String[] excludes() default {};
}
@Aspect
@Component
public class XssSecurityAspect {
  private static final Logger logger = LoggerFactory.getLogger(XssSecurityAspect.class);
  @Pointcut("@annotation(cn.example.demo.annotation.XssSecurity)")
  public void xssPointCut() {
  }
  @Before("xssPointCut() && @annotation(xssSecurity)")
  public void doBefore(JoinPoint joinPoint, XssSecurity xssSecurity) throws Throwable {
    ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
    if (servletRequestAttributes == null) {
        return;
    }
    HttpServletRequest request = servletRequestAttributes.getRequest();
    if (checkUrlExclude(request.getRequestURI(), xssSecurity.excludes())) {
        return;
    }
    XssHttpServletRequestWrapper xssRequest = new XssHttpServletRequestWrapper(request);
    request.setAttribute("body", xssRequest.getBody());
    for (Object arg : joinPoint.getArgs()) {
        if (arg instanceof XssHttpServletRequestWrapper || arg instanceof MultipartFile) {
            continue;
        }
        Object o = xssSecurity(xssRequest, arg);
        Method method = reflectMethod(joinPoint, arg);
        if (null != method) {
            method.invoke(joinPoint.getTarget(), o);
        }
    }
  }
  public Object xssSecurity(HttpServletRequest request, Object param) {
    if (param == null) {
        return null;
    }
    if (param instanceof String) {
        return HtmlUtils.htmlEscape((String) param);
    } else if (param instanceof Map) {
        Map在这个示例中,我们使用了AOP的方式来处理XSS过滤,通过自定义注解@XssSecurity来实现。在XssSecurityAspect的实现中,我们通过@Pointcut(“@annotation(cn.example.demo.annotation.XssSecurity)”)注解来定义Pointcut。在doBefore方法中,主要是通过反射的方式,对请求进行过滤。我们通过checkUrlExclude方法来判断当前请求是否在排除列表中,如果在就不进行过滤。
四、Spring-boot-starter-validation
1、Spring-boot-starter-validation介绍
Spring-boot-starter-validation是Springboot提供的JSR303验证依赖库,可以非常方便的对请求数据进行校验,包括数据类型、参数是否为空、参数是否在指定的范围内等等。使用起来非常方便简单。
2、Spring-boot-starter-validation示例
以下代码演示了如何使用Spring-boot-starter-validation的@NotBlank注解进行参数的非空判断:
@Data
@NoArgsConstructor
@AllArgsConstructor
public class UserDTO {
    @NotBlank(message = "用户名不能为空")
    private String name;
}
@RestController
public class UserController {
    @PostMapping("/user")
    public void addUser(@Validated @RequestBody UserDTO userDTO) {
        // Todo
    }
}
在这个示例中,我们使用了@NotBlank(message = “用户名不能为空”)注解对UserDTO中的name字段进行了非空判断。如果请求中的name为空,Springboot会返回错误信息”用户名不能为空”,并返回400响应码。
五、Spring Security过滤器链
1、Spring Security介绍
Spring Security是Spring官方的安全框架,提供了一系列的安全服务,包括权限认证、资源保护等,Spring Security内置了很多过滤器,可以通过配置来加入过滤器链进行XSS过滤。
2、Spring Security示例
以下代码演示了如何在Spring Security配置中添加XSS过滤器来保护应用:
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Bean
    public FilterRegistrationBean xssFilterRegistrationBean() {
        FilterRegistrationBean registrationBean = new FilterRegistrationBean();
        registrationBean.setFilter(new XssFilter());
        registrationBean.addUrlPatterns("/*");
        registrationBean.setName("xssFilter");
        return registrationBean;
    }
}
我们在SecurityConfig中通过@Bean注解,加入了一个名为xssFilter的过滤器,把这个过滤器添加到了Spring Security的过滤器链中。这个过滤器实现方式与第一部分中的过滤器是一样的,都是通过XssHttpServletRequestWrapper来过滤请求。
六、小结
通过以上介绍,我们可以看出Springboot提供了多种方式进行XSS过滤,包括过滤器、注解、自定义注解和结合Spring Security等。不同的过滤方式有各自的优缺点和使用场景,开发人员可以结合具体需求来选择最合适的方式进行过滤。XSS攻击是一种非常常见且危险的攻击方式,合理的XSS过滤能够保障系统的安全稳定。
原创文章,作者:小蓝,如若转载,请注明出处:https://www.506064.com/n/180278.html
 
 微信扫一扫
微信扫一扫  支付宝扫一扫
支付宝扫一扫 