业务服务:xss攻击

文章目录

  • 前言
  • 一、使用注解预防
    • [1. 添加依赖](#1. 添加依赖)
    • [2. 自定义注解](#2. 自定义注解)
    • [3. 自定义校验逻辑](#3. 自定义校验逻辑)
    • [4. 使用](#4. 使用)
  • 二、使用过滤器
    • [1. 添加配置](#1. 添加配置)
    • [2. 创建配置类](#2. 创建配置类)
    • [3. 创建过滤器](#3. 创建过滤器)
    • [4. 创建过滤器类](#4. 创建过滤器类)
    • [5. 使用](#5. 使用)

前言

xss攻击时安全领域中非常常见的一种方法,保证我们的系统安全是非常重要的

xss攻击简单来说就是在用户输入内容中添加脚本< script >...< script >

这里面可能包含获取cookie,


一、使用注解预防

1. 添加依赖

xml 复制代码
<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-validation</artifactId>
</dependency>

2. 自定义注解

java 复制代码
@Retention(RetentionPolicy.RUNTIME)
@Target(value = {ElementType.METHOD, ElementType.FIELD, ElementType.CONSTRUCTOR, ElementType.PARAMETER})
@Constraint(validatedBy = {XssValidator.class})
public @interface Xss {

    String message() default "不允许任何脚本运行";

    Class<?>[] groups() default {};

    Class<? extends Payload>[] payload() default {};

}

3. 自定义校验逻辑

java 复制代码
public class XssValidator implements ConstraintValidator<Xss, String> {

    @Override
    public boolean isValid(String value, ConstraintValidatorContext constraintValidatorContext) {
    	// 这里用的hutool的工具类
        return !ReUtil.contains(HtmlUtil.RE_HTML_MARK, value);
    }

}

4. 使用

创建实体类

java 复制代码
@Data
public class Book {

    private Long id;

    private String name;

    @Xss
    private String content;
}

创建book控制器

java 复制代码
@Validated
@RestController
@RequestMapping("/book")
public class BookController {



    @PostMapping
    public void save(@Validated @RequestBody Book book){
        System.out.println(book);
    }
}

发送请求

可以看到系统抛出了异常,这样我们就成功了使用注解完成了脚本验证

二、使用过滤器

注解的方式需要一个一个的添加,这显然是不太方便的。我们可以通过过滤器的方式对前端传递过来的参数进行统一处理

1. 添加配置

java 复制代码
# 防止XSS攻击
xss:
  # 过滤开关
  enabled: true
  # 排除链接(多个用逗号分隔)
  excludes: 
  # 匹配链接
  urlPatterns: /book/*

2. 创建配置类

java 复制代码
@Data
@Component
@ConfigurationProperties(prefix = "xss")
public class XssProperties {

    /**
     * 过滤开关
     */
    private String enabled;

    /**
     * 排除链接(多个用逗号分隔)
     */
    private String excludes;

    /**
     * 匹配链接
     */
    private String urlPatterns;

}

3. 创建过滤器

java 复制代码
@Configuration
public class FilterConfig {

    @Autowired
    private XssProperties xssProperties;

	// 关闭校验注解
    @SuppressWarnings({"rawtypes", "unchecked"})
    @Bean
    // xss.enabled==true时,注入bean
    @ConditionalOnProperty(value = "xss.enabled", havingValue = "true") 
    public FilterRegistrationBean xssFilterRegistration() {
    	// 创建过滤器注册器
        FilterRegistrationBean registration = new FilterRegistrationBean();
		
		// 设置运行类型
        registration.setDispatcherTypes(DispatcherType.REQUEST);
		
		// 设置过滤器
        registration.setFilter(new XssFilter());
	
		// 添加拦截路径
        registration.addUrlPatterns(StrUtil.split(xssProperties.getUrlPatterns(), StrUtil.C_COMMA).toArray(String[]::new));
        registration.setName("xssFilter");

		// 设置优先级为最高
        registration.setOrder(FilterRegistrationBean.HIGHEST_PRECEDENCE);

		// 添加自定义参数
        Map<String, String> initParameters = new HashMap<String, String>();
        initParameters.put("excludes", xssProperties.getExcludes());
        registration.setInitParameters(initParameters);

        return registration;
    }
}

4. 创建过滤器类

java 复制代码
public class XssFilter implements Filter {
    /**
     * 排除链接
     */
    public List<String> excludes = new ArrayList<>();

    /**
     * 初始化的时候将排除连接根据,分割添加到excludes中
     * @param filterConfig
     * @throws ServletException
     */
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        String tempExcludes = filterConfig.getInitParameter("excludes");
        if (StrUtil.isNotBlank(tempExcludes)) {
            String[] url = tempExcludes.split(StrUtil.COMMA);
            excludes.addAll(Arrays.asList(url));
        }
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
        throws IOException, ServletException {
        HttpServletRequest req = (HttpServletRequest) request;
        HttpServletResponse resp = (HttpServletResponse) response;
        if (handleExcludeURL(req, resp)) {
            chain.doFilter(request, response);
            return;
        }
        XssHttpServletRequestWrapper xssRequest = new XssHttpServletRequestWrapper((HttpServletRequest) request);
        chain.doFilter(xssRequest, response);
    }

    /**
     * 判断是否为排除过滤路径
     * @param request
     * @param response
     * @return
     */
    private boolean handleExcludeURL(HttpServletRequest request, HttpServletResponse response) {
        String url = request.getServletPath();
        String method = request.getMethod();
        // GET DELETE 不过滤
        if (method == null || HttpMethod.GET.matches(method) || HttpMethod.DELETE.matches(method)) {
            return true;
        }
        return matches(url, excludes);
    }
    public static boolean matches(String str, List<String> strs) {
        if (StrUtil.isBlank(str) || CollUtil.isEmpty(strs)) {
            return false;
        }
        for (String pattern : strs) {
            if (isMatch(pattern, str)) {
                return true;
            }
        }
        return false;
    }
    public static boolean isMatch(String pattern, String url) {
        AntPathMatcher matcher = new AntPathMatcher();
        return matcher.match(pattern, url);
    }

    @Override
    public void destroy() {

    }
}
java 复制代码
public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper {
    /**
     * @param request
     */
    public XssHttpServletRequestWrapper(HttpServletRequest request) {
        super(request);
    }

    /**
     * 将url拼接的参数进行脚本过滤
     * @param name
     * @return
     */
    @Override
    public String[] getParameterValues(String name) {
        String[] values = super.getParameterValues(name);
        if (values != null) {
            int length = values.length;
            String[] escapesValues = new String[length];
            for (int i = 0; i < length; i++) {
                // 防xss攻击和过滤前后空格
                escapesValues[i] = HtmlUtil.cleanHtmlTag(values[i]).trim();
            }
            return escapesValues;
        }
        return super.getParameterValues(name);
    }

    /**
     * 对body的脚本参数进行过滤
     * @return
     * @throws IOException
     */
    @Override
    public ServletInputStream getInputStream() throws IOException {
        // 非json类型,直接返回
        if (!isJsonRequest()) {
            return super.getInputStream();
        }

        // 为空,直接返回
        String json = StrUtil.str(IoUtil.readBytes(super.getInputStream(), false), StandardCharsets.UTF_8);
        if (StringUtils.isEmpty(json)) {
            return super.getInputStream();
        }

        // xss过滤
        json = HtmlUtil.cleanHtmlTag(json).trim();
        byte[] jsonBytes = json.getBytes(StandardCharsets.UTF_8);
        final ByteArrayInputStream bis = IoUtil.toStream(jsonBytes);
        return new ServletInputStream() {
            @Override
            public boolean isFinished() {
                return true;
            }

            @Override
            public boolean isReady() {
                return true;
            }

            @Override
            public int available() throws IOException {
                return jsonBytes.length;
            }

            @Override
            public void setReadListener(ReadListener readListener) {
            }

            @Override
            public int read() throws IOException {
                return bis.read();
            }
        };
    }

    /**
     * 是否是Json请求
     */
    public boolean isJsonRequest() {
        String header = super.getHeader(HttpHeaders.CONTENT_TYPE);
        return StringUtils.startsWithIgnoreCase(header, MediaType.APPLICATION_JSON_VALUE);
    }
}

5. 使用

发送请求

可以看到传递的脚本被成功过滤掉

相关推荐
RainbowSea7 分钟前
用户中心——比如:腾讯的QQ账号可以登录到很多应用当中 02
java·spring boot·mysql
RainbowSea13 分钟前
用户中心——比如:腾讯的QQ账号可以登录到很多应用当中 01
java·spring boot·mysql
超浪的晨20 分钟前
Java List 集合详解:从基础到实战,掌握 Java 列表操作全貌
java·开发语言·后端·学习·个人开发
Mintopia25 分钟前
🌀曲面细分求交:在无限细节中捕捉交点的浪漫
前端·javascript·计算机图形学
超浪的晨25 分钟前
Java Set 集合详解:从基础语法到实战应用,彻底掌握去重与唯一性集合
java·开发语言·后端·学习·个人开发
Mintopia28 分钟前
🧙‍♂️用 Three.js 判断一个点是否在圆内 —— 一次圆心和点的对话
前端·javascript·three.js
liliangcsdn43 分钟前
mac mlx大模型框架的安装和使用
java·前端·人工智能·python·macos
CssHero1 小时前
基于vue3完成领域模型架构建设
前端
PanZonghui1 小时前
用项目说话:我的React博客构建成果与经验复盘
前端·react.js·typescript
言兴1 小时前
教你如何理解useContext加上useReducer
前端·javascript·面试