马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
×
在Spring Boot中,我们可以利用注解的方式来举行XSS防御。注解是一种轻量级的防御本领,它可以在方法或字段级别对输入举行校验,从而防止XSS攻击。
而想对全局的哀求都举行XSS防御可以利用servlet中的过滤器大概spring mvc中的拦截器,这里利用servlet中的过滤器举行演示。
引入干系依靠
maven依靠:- <!--JSR-303/JSR-380用于验证的注解 -->
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-validation</artifactId>
- <version>2.6.7</version>
- </dependency>
复制代码 假如是利用grade,引入依靠:- implementation 'org.springframework.boot:spring-boot-starter-validation:2.6.7'
复制代码 修改设置文件
- xss:
- enabled: true
- excludeUrlList:
- - /xss/local/test
复制代码 界说设置文件对应的属性类
- 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设置类
- 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。- 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哀求和哀求头中的非法字符。- 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哀求
- 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过滤工具类
- 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哀求测试
- 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哀求测试
- 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"
- }
复制代码 |