业务服务: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. 使用

发送请求

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

相关推荐
掘了14 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅14 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅14 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
爬山算法14 小时前
Hibernate(90)如何在故障注入测试中使用Hibernate?
java·后端·hibernate
kfyty72514 小时前
集成 spring-ai 2.x 实践中遇到的一些问题及解决方案
java·人工智能·spring-ai
猫头虎14 小时前
如何排查并解决项目启动时报错Error encountered while processing: java.io.IOException: closed 的问题
java·开发语言·jvm·spring boot·python·开源·maven
李少兄15 小时前
在 IntelliJ IDEA 中修改 Git 远程仓库地址
java·git·intellij-idea
崔庆才丨静觅15 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment15 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅15 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端