广告位联系
返回顶部
分享到

SpringBoot怎么使用过滤器进行XSS防御

java 来源:互联网 作者:佚名 发布时间:2024-11-16 10:41:36 人浏览
摘要

在Spring Boot中,我们可以使用注解的方式来进行XSS防御。注解是一种轻量级的防御手段,它可以在方法或字段级别对输入进行校验,从而防止XSS攻击。 而想对全局的请求都进行XSS防御可以使用

在Spring Boot中,我们可以使用注解的方式来进行XSS防御。注解是一种轻量级的防御手段,它可以在方法或字段级别对输入进行校验,从而防止XSS攻击。

而想对全局的请求都进行XSS防御可以使用servlet中的过滤器或者spring mvc中的拦截器,这里使用servlet中的过滤器进行演示。

引入相关依赖

maven依赖:

1

2

3

4

5

6

<!--JSR-303/JSR-380用于验证的注解 -->

<dependency>

    <groupId>org.springframework.boot</groupId>

    <artifactId>spring-boot-starter-validation</artifactId>

    <version>2.6.7</version>

</dependency>

如果是使用grade,引入依赖:

1

implementation 'org.springframework.boot:spring-boot-starter-validation:2.6.7'

修改配置文件

1

2

3

4

xss:

  enabled: true

  excludeUrlList:

    - /xss/local/test

定义配置文件对应的属性类

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

package com.morris.spring.boot.module.xss;

 

import lombok.Data;

 

import java.util.List;

 

@Data

public class XssFilterProperties {

    /**

     * 是否启用XSS过滤。

     */

    private boolean enabled = true;

 

    /**

     * 需要排除的URL模式,这些URL不会进行XSS过滤。

     */

    private List<String> excludeUrlList;

}

注入XSS配置类

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

package com.morris.spring.boot.module.xss;

 

import lombok.Data;

import org.springframework.boot.autoconfigure.http.HttpMessageConverters;

import org.springframework.boot.context.properties.ConfigurationProperties;

import org.springframework.boot.web.servlet.FilterRegistrationBean;

import org.springframework.context.annotation.Bean;

import org.springframework.context.annotation.Configuration;

import org.springframework.http.converter.HttpMessageConverter;

 

import javax.servlet.DispatcherType;

 

@Data

@Configuration

public class XssFilterConfig {

 

    @ConfigurationProperties(prefix = "xss")

    @Bean

    public XssFilterProperties xssFilterProperties() {

        return new XssFilterProperties();

    }

 

    /**

     * 注册XSS过滤器。

     *

     * @return FilterRegistrationBean 用于注册过滤器的bean。

     */

    @Bean

    public FilterRegistrationBean<XssFilter> xssFilterRegistration(XssFilterProperties xssFilterProperties) {

        FilterRegistrationBean<XssFilter> registrationBean = new FilterRegistrationBean<>();

        // 设置过滤器的分发类型为请求类型

        registrationBean.setDispatcherTypes(DispatcherType.REQUEST);

        // 创建XssFilter的实例

        registrationBean.setFilter(new XssFilter(xssFilterProperties));

        // 添加过滤器需要拦截的URL模式,这里拦截所有请求

        registrationBean.addUrlPatterns("/*");

        // 设置过滤器的名称

        registrationBean.setName("XssFilter");

        // 设置过滤器的执行顺序,数值越小,优先级越高

        registrationBean.setOrder(9999);

        return registrationBean;

    }

 

    @Bean

    public HttpMessageConverters xssHttpMessageConverters() {

        XSSMappingJackson2HttpMessageConverter xssMappingJackson2HttpMessageConverter = new XSSMappingJackson2HttpMessageConverter();

        HttpMessageConverter converter = xssMappingJackson2HttpMessageConverter;

        return new HttpMessageConverters(converter);

    }

 

}

XssFilter过滤器

XssFilter过滤器会将所有需要进行防御的请求包装为XssWrapper。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

package com.morris.spring.boot.module.xss;

 

import lombok.extern.slf4j.Slf4j;

import org.springframework.util.CollectionUtils;

 

import javax.servlet.*;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

import java.io.IOException;

import java.util.regex.Matcher;

import java.util.regex.Pattern;

 

@Slf4j

public class XssFilter implements Filter {

 

    private final XssFilterProperties xssFilterProperties;

 

    public XssFilter(XssFilterProperties xssFilterProperties) {

        this.xssFilterProperties = xssFilterProperties;

    }

 

    /**

     * 执行过滤逻辑,如果当前请求不在排除列表中,则通过XSS过滤器包装请求。

     *

     * @param request  HTTP请求对象。

     * @param response HTTP响应对象。

     * @param chain    过滤器链对象,用于继续或中断请求处理。

     * @throws IOException      如果处理过程中出现I/O错误。

     * @throws ServletException 如果处理过程中出现Servlet相关错误。

     */

    @Override

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

        HttpServletRequest req = (HttpServletRequest) request;

        HttpServletResponse resp = (HttpServletResponse) response;

        //如果该访问接口在排除列表里面则不拦截

        if (isExcludeUrl(req.getServletPath())) {

            chain.doFilter(request, response);

            return;

        }

 

        log.info("uri:{}", req.getRequestURI());

        // xss 过滤

        chain.doFilter(new XssWrapper(req), resp);

    }

 

    /**

     * 判断当前请求的URL是否应该被排除在XSS过滤之外。

     *

     * @param urlPath 请求的URL路径。

     * @return 如果请求应该被排除,则返回true;否则返回false。

     */

    private boolean isExcludeUrl(String urlPath) {

        if (!xssFilterProperties.isEnabled()) {

            //如果xss开关关闭了,则所有url都不拦截

            return true;

        }

 

        if(CollectionUtils.isEmpty(xssFilterProperties.getExcludeUrlList())) {

            return false;

        }

 

        for (String pattern : xssFilterProperties.getExcludeUrlList()) {

            Pattern p = Pattern.compile("^" + pattern);

            Matcher m = p.matcher(urlPath);

            if (m.find()) {

                return true;

            }

        }

        return false;

    }

}

XssWrapper过滤get请求和请求头

XssWrapper会过滤get请求和请求头中的非法字符。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

package com.morris.spring.boot.module.xss;

 

import lombok.extern.slf4j.Slf4j;

import org.apache.commons.lang3.StringUtils;

 

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletRequestWrapper;

 

@Slf4j

public class XssWrapper extends HttpServletRequestWrapper {

 

    public XssWrapper(HttpServletRequest request) {

        super(request);

    }

 

    /**

     * 对数组参数进行特殊字符过滤

     */

    @Override

    public String[] getParameterValues(String name) {

        String[] values = super.getParameterValues(name);

        if (values == null) {

            return null;

        }

        int count = values.length;

        String[] encodedValues = new String[count];

        for (int i = 0; i < count; i++) {

            encodedValues[i] = XssUtil.clean(values[i]);

        }

        return encodedValues;

    }

 

    /**

     * 对参数中特殊字符进行过滤

     */

    @Override

    public String getParameter(String name) {

        String value = super.getParameter(name);

        if (StringUtils.isBlank(value)) {

            return value;

        }

        return XssUtil.clean(value);

    }

 

    /**

     * 获取attribute,特殊字符过滤

     */

    @Override

    public Object getAttribute(String name) {

        Object value = super.getAttribute(name);

        if (value instanceof String && StringUtils.isNotBlank((String) value)) {

            return XssUtil.clean((String) value);

        }

        return value;

    }

 

    /**

     * 对请求头部进行特殊字符过滤

     */

    @Override

    public String getHeader(String name) {

        String value = super.getHeader(name);

        if (StringUtils.isBlank(value)) {

            return value;

        }

        return XssUtil.clean(value);

    }

}

MessageConverter过滤post请求

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

package com.morris.spring.boot.module.xss;

 

import com.fasterxml.jackson.databind.JavaType;

import org.springframework.http.HttpInputMessage;

import org.springframework.http.HttpOutputMessage;

import org.springframework.http.converter.HttpMessageNotReadableException;

import org.springframework.http.converter.HttpMessageNotWritableException;

import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;

 

import java.io.IOException;

import java.lang.reflect.Type;

 

/**

 * 在读取和写入JSON数据时特殊字符避免xss攻击的消息解析器

 *

 */

public class XSSMappingJackson2HttpMessageConverter extends MappingJackson2HttpMessageConverter {

 

    /**

     * 从HTTP输入消息中读取对象,同时应用XSS防护。

     *

     * @param type        类型令牌,表示要读取的对象类型。

     * @param contextClass    上下文类,提供类型解析的上下文信息。

     * @param inputMessage HTTP输入消息,包含要读取的JSON数据。

     * @return 从输入消息中解析出的对象,经过XSS防护处理。

     * @throws IOException 如果发生I/O错误。

     * @throws HttpMessageNotReadableException 如果消息无法读取。

     */

    @Override

    public Object read(Type type, Class contextClass,

                       HttpInputMessage inputMessage) throws IOException,

            HttpMessageNotReadableException {

        JavaType javaType = getJavaType(type, contextClass);

        Object obj = readJavaType(javaType, inputMessage);

        //得到请求json

        String json = super.getObjectMapper().writeValueAsString(obj);

        //过滤特殊字符

        String result = XssUtil.clean(json);

        Object resultObj = super.getObjectMapper().readValue(result, javaType);

        return resultObj;

    }

 

    /**

     * 从HTTP输入消息中读取指定Java类型的对象,内部使用。

     *

     * @param javaType    要读取的对象的Java类型。

     * @param inputMessage HTTP输入消息,包含要读取的JSON数据。

     * @return 从输入消息中解析出的对象。

     * @throws IOException 如果发生I/O错误。

     * @throws HttpMessageNotReadableException 如果消息无法读取。

     */

    private Object readJavaType(JavaType javaType, HttpInputMessage inputMessage) {

        try {

            return super.getObjectMapper().readValue(inputMessage.getBody(), javaType);

        } catch (IOException ex) {

            throw new HttpMessageNotReadableException("Could not read JSON: " + ex.getMessage(), ex);

        }

    }

 

    /**

     * 将对象写入HTTP输出消息,同时应用XSS防护。

     *

     * @param object 要写入的对象。

     * @param outputMessage HTTP输出消息,对象将被序列化为JSON并写入此消息。

     * @throws IOException 如果发生I/O错误。

     * @throws HttpMessageNotWritableException 如果消息无法写入。

     */

    @Override

    protected void writeInternal(Object object, HttpOutputMessage outputMessage)

            throws IOException, HttpMessageNotWritableException {

        //得到要输出的json

        String json = super.getObjectMapper().writeValueAsString(object);

        //过滤特殊字符

        String result = XssUtil.clean(json);

        // 输出

        outputMessage.getBody().write(result.getBytes());

    }

}

Xss过滤工具类

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

package com.morris.spring.boot.module.xss;

 

import org.jsoup.Jsoup;

import org.jsoup.nodes.Document;

import org.jsoup.safety.Whitelist;

 

/**

 * XSS过滤工具类,使用Jsoup库对输入的字符串进行XSS攻击防护

 */

public class XssUtil {

 

    /**

     * 使用jsoup自带的relaxed白名单

     */

    private static final Whitelist WHITE_LIST = Whitelist.relaxed();

    /**

     * 定义输出设置,关闭prettyPrint(prettyPrint=false),目的是避免在清理过程中对代码进行格式化

     * 从而保持输入和输出内容的一致性。

     */

    private static final Document.OutputSettings OUTPUT_SETTINGS = new Document.OutputSettings().prettyPrint(false);

 

    /*

      初始化白名单策略,允许所有标签拥有style属性。

      这是因为在富文本编辑中,样式通常通过style属性来定义,需要确保这些样式能够被保留。

     */

    static {

        // 富文本编辑时一些样式是使用 style 来进行实现的

        // 比如红色字体 style="color:red;"

        // 所以需要给所有标签添加 style 属性

        WHITE_LIST.addAttributes(":all", "style");

    }

 

    /**

     * 清理输入的字符串,移除潜在的XSS攻击代码。

     *

     * @param content 待清理的字符串,通常是用户输入的HTML内容。

     * @return 清理后的字符串,保证不包含XSS攻击代码。

     */

    public static String clean(String content) {

        // 使用定义好的白名单策略和输出设置清理输入的字符串

        return Jsoup.clean(content, "", WHITE_LIST, OUTPUT_SETTINGS);

    }

}

get请求测试

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

package com.morris.spring.boot.module.xss;

 

import org.springframework.validation.annotation.Validated;

import org.springframework.web.bind.annotation.GetMapping;

import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.RestController;

 

 

/**

 * Xss2防御get请求

 */

@RestController

@RequestMapping("/xss/global")

@Validated

public class XssGlobalGetController {

 

    /**

     * 使用注解拦截get请求中的xss,在方法参数前面加上@Xss,注意类上面要加上@Validated注解

     *

     * @param userAccount 请求参数

     * @return 请求参数

     */

    @GetMapping("/test")

    public String test(String userAccount) {

        return userAccount;

    }

 

}

发送get请求:http://localhost:8888/xss/global/test?userAccount=demoData

返回结果:demoData

post请求测试

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

package com.morris.spring.boot.module.xss;

 

import org.springframework.web.bind.annotation.PostMapping;

import org.springframework.web.bind.annotation.RequestBody;

import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.RestController;

 

/**

 * Xss全局防御post请求

 */

@RestController

@RequestMapping("/xss/global")

public class XssGlobalPostController {

 

    /**

     * 使用注解拦截POST请求中的xss,在实体类需要拦截xss的属性上面加上@Xss或者@Validated注解

     *

     * @param userGlobalLoginPojo 实体类

     * @return 实体类

     */

    @PostMapping("/test")

    public UserGlobalLoginPojo test(@RequestBody UserGlobalLoginPojo userGlobalLoginPojo) {

        return userGlobalLoginPojo;

    }

 

}

发送post请求:http://localhost:8888/xss/global/test

请求体:

{
    "userAccount": "<iframe οnlοad='alert(0)'>demoData</iframe>"
}

返回结果:

{
    "userAccount": "demoData"
}


版权声明 : 本文内容来源于互联网或用户自行发布贡献,该文观点仅代表原作者本人。本站仅提供信息存储空间服务和不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权, 违法违规的内容, 请发送邮件至2530232025#qq.cn(#换@)举报,一经查实,本站将立刻删除。
原文链接 :
相关文章
  • 本站所有内容来源于互联网或用户自行发布,本站仅提供信息存储空间服务,不拥有版权,不承担法律责任。如有侵犯您的权益,请您联系站长处理!
  • Copyright © 2017-2022 F11.CN All Rights Reserved. F11站长开发者网 版权所有 | 苏ICP备2022031554号-1 | 51LA统计