Springboot过滤xss

Springboot过滤xss

两种xss类型:存储型xss、反射型xss。

简介:

存储型:持久化,代码是存储在服务器中的,如在个人信息或发表文章等地方,加入代码,如果没有过滤或过滤不严,那么这些代码将储存到服务器中,用户访问该页面的时候触发代码执行。这种XSS比较危险,容易造成蠕虫,盗窃cookie等

反射型:非持久化,需要欺骗用户自己去点击链接才能触发XSS代码(服务器中没有这样的页面和内容),一般容易出现在搜索页面。

问题产生:

公司代码发布前需要进行3部分安全扫描,其中第一个就出现这种问题了。如图。

解决方法:

首先springboot后台常见的接收参数一般有三种方式:@RequestParam 、 @PathVariable 、 @RequestBody

(1).过滤表单传值(?传参 即  @RequestParam)

step1:创建包装request的类 XssHttpServletRequestWrapper

package com.cpic.config.xss;

import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringEscapeUtils;

import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.Charset;

//参考:https://www.freesion.com/article/7503775029/
//过滤表单传值(?传参 即  @RequestParam)
public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper {

    public XssHttpServletRequestWrapper(HttpServletRequest request) {
        super(request);
    }

    @Override
    public String getQueryString() {
        System.out.println("11===");
        return StringEscapeUtils.escapeHtml4(super.getQueryString());
    }

    @Override
    public String getParameter(String name) {
        System.out.println("22===");
        return StringEscapeUtils.escapeHtml4(super.getParameter(name));
    }

    @Override
    public String[] getParameterValues(String name) {
        //过滤表单传值(?传参 即  @RequestParam)
        System.out.println("33===");
        String[] values = super.getParameterValues(name);
        if (ArrayUtils.isEmpty(values)) {
            return values;
        }
        int length = values.length;
        String[] escapeValues = new String[length];
        for (int i = 0; i < length; i++) {
            escapeValues[i] = StringEscapeUtils.escapeHtml4(values[i]);
        }
        return escapeValues;
    }


    //-------------------补充(考虑到部分参数会在header里面传过来)------------------
    //参考:https://blog.csdn.net/weixin_38497019/article/details/95940285
    @Override
    public String getHeader(String name) {
        String value = super.getHeader(name);
        if (value == null)
            return null;
        return StringEscapeUtils.escapeHtml4(value);
    }

    @Override
    public ServletInputStream getInputStream() throws IOException {
        final ByteArrayInputStream bais = new ByteArrayInputStream(inputHandlers(super.getInputStream ()).getBytes ());

        return new ServletInputStream() {

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

            @Override
            public boolean isFinished() {
                return false;
            }

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

            @Override
            public void setReadListener(ReadListener readListener) { }
        };
    }

    public   String inputHandlers(ServletInputStream servletInputStream){
        StringBuilder sb = new StringBuilder();
        BufferedReader reader = null;
        try {
            reader = new BufferedReader(new InputStreamReader(servletInputStream, Charset.forName("UTF-8")));
            String line = "";
            while ((line = reader.readLine()) != null) {
                sb.append(line);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (servletInputStream != null) {
                try {
                    servletInputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (reader != null) {
                try {
                    reader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return  StringEscapeUtils.escapeHtml4(sb.toString ());
    }

   //如果不喜欢 使用StringEscapeUtils.escapeHtml4 的方法可以采取类似下面自定义的方法
/*    private static String cleanXSS(String value) {
        value = value.replaceAll("<", "&lt;").replaceAll(">", "&gt;");
        value = value.replaceAll("%3C", "&lt;").replaceAll("%3E", "&gt;");
        value = value.replaceAll("\\(", "&#40;").replaceAll("\\)", "&#41;");
        value = value.replaceAll("%28", "&#40;").replaceAll("%29", "&#41;");
        value = value.replaceAll("'", "&#39;");
        value = value.replaceAll("eval\\((.*)\\)", "");
        value = value.replaceAll("[\\\"\\\'][\\s]*javascript:(.*)[\\\"\\\']", "\"\"");
        value = value.replaceAll("script", "");
        return value;
    }*/

}

step2:创建自定义过滤器XssFilter过滤请求

package com.cpic.config.xss;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;

//参考:https://www.freesion.com/article/7503775029/
//过滤表单传值(?传参 即  @RequestParam)
@WebFilter(filterName="XSSFilter", urlPatterns="/*")
public class XssFilter implements Filter {

    FilterConfig filterConfig = null;

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("过滤器初始化");
        this.filterConfig = filterConfig;
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        System.out.println("执行过滤操作");
        filterChain.doFilter(new XssHttpServletRequestWrapper((HttpServletRequest) servletRequest), servletResponse);
    }

    @Override
    public void destroy() {
        System.out.println("过滤器销毁");
        this.filterConfig = null;
    }

}

step3:在springboot启动类添加过滤器扫描(@ServletComponentScan("com.cpic.config.xss")这个就是上面自定义过滤器的位置)

@EnableScheduling
@EnableAsync
@SpringBootApplication(exclude={RedisAutoConfiguration.class,RedisRepositoriesAutoConfiguration.class})
@ServletComponentScan("com.cpic.config.xss")
//过滤表单传值(?传参 即  @RequestParam)
public class Application extends SpringBootServletInitializer {
   
    public static void main(String[] args) {
        SpringApplication springApplication = new SpringApplication(Application.class);
        springApplication.setBannerMode(Mode.OFF);
        springApplication.run(args);
    }
    
    protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
        return application.sources(Application.class);
    }
    
}
(2).过滤类似 @GetMapping("/testXss/{openId}")(url 后面/传参 即  @PathVariable)

step:创建类 XssHandlerMappingPostProcessor

 

package com.cpic.config.xss;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.MultiValueMap;
import org.springframework.web.servlet.handler.AbstractHandlerMapping;
import org.springframework.web.util.HtmlUtils;
import org.springframework.web.util.UrlPathHelper;

import javax.servlet.http.HttpServletRequest;
import java.util.List;
import java.util.Map;

//https://blog.csdn.net/whatzhang007/article/details/111589693
//http://www.chaiguanxin.com/articles/2020/03/18/1584495603475.html
//过滤url后面的/传值(/传参 即  @PathVariable)
@Component
public class XssHandlerMappingPostProcessor implements BeanPostProcessor {

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException{
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException{
        if(bean instanceof AbstractHandlerMapping){
            AbstractHandlerMapping ahm = (AbstractHandlerMapping) bean;
            ahm.setUrlPathHelper(new XssUrlPathHelper());
        }

        return bean;
    }

    static class XssUrlPathHelper extends UrlPathHelper {

        @Override
        public Map<String, String> decodePathVariables(HttpServletRequest request, Map<String, String> vars){
            Map<String, String> result = super.decodePathVariables(request, vars);
            if(!CollectionUtils.isEmpty(result)){
                for(String key : result.keySet()){
                    result.put(key, cleanXSS(result.get(key)));
                }
            }

            return result;
        }

        @Override
        public MultiValueMap<String, String> decodeMatrixVariables(HttpServletRequest request,
                                                                   MultiValueMap<String, String> vars){
            MultiValueMap<String, String> mvm = super.decodeMatrixVariables(request, vars);
            if(!CollectionUtils.isEmpty(mvm)){
                for(String key : mvm.keySet()){
                    List<String> value = mvm.get(key);
                    for(int i = 0; i < value.size(); i++){
                        value.set(i, cleanXSS(value.get(i)));
                    }
                }
            }

            return mvm;
        }

        private String cleanXSS(String value){
            return HtmlUtils.htmlEscape(value);
        }

    }

}
(3).过滤json传值(json传参 即  @RequestBody)

step:创建类WebConfig

package com.cpic.config.webmvc;

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import org.apache.commons.lang3.StringEscapeUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import java.io.IOException;

//https://www.freesion.com/article/7503775029/
//过滤post请求json传值(@RequestBody)
@Configuration
public class WebConfig implements WebMvcConfigurer, InitializingBean {
    /**
     * 默认就是@Autowired(required=true),表示注入的时候,该bean必须存在,否则就会注入失败required = false,表示忽略当前要注入的bean,如果有直接注入,没有跳过,不会报错
     */
    @Autowired(required = false)
    private ObjectMapper objectMapper;

    private SimpleModule getSimpleModule() {
        SimpleModule simpleModule = new SimpleModule();
        simpleModule.addDeserializer(String.class, new JsonHtmlXssDeserializer(String.class));
        return simpleModule;
    }

    /**
     * 初始化bean的时候执行,可以针对某个具体的bean进行配置。afterPropertiesSet 必须实现 InitializingBean接口。实现 InitializingBean接口必须实现afterPropertiesSet方法
     * 这个方法将在所有的属性被初始化后调用,但是会在init前调用
     * @throws Exception
     */
    @Override
    public void afterPropertiesSet() throws Exception {
        if (objectMapper != null) {
            SimpleModule simpleModule = getSimpleModule();
            objectMapper.registerModule(simpleModule);
        }
    }
}

/**
 * 对入参的json进行转义
 */
class JsonHtmlXssDeserializer extends JsonDeserializer<String> {

    public JsonHtmlXssDeserializer(Class<String> string) {
        super();
    }

    @Override
    public Class<String> handledType() {
        return String.class;
    }

    @Override
    public String deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
        String value = jsonParser.getValueAsString();
        if (value != null) {
            return StringEscapeUtils.escapeHtml4(value.toString());
        }
        return value;
    }
}
(4).过滤?和/传值(即  @RequestParam和@PathVariable)

由于?和/实际上都是字符串接收参数,可以定义一个字符串转换器,让后配置到mvcConfig

step1:创建类EscapeStringConverter

package com.cpic.config.xss;

import org.apache.commons.lang3.StringUtils;
import org.springframework.core.convert.converter.Converter;
import org.springframework.stereotype.Component;
import org.springframework.web.util.HtmlUtils;

//http://www.10qianwan.com/articledetail/766291.html
@Component
public class EscapeStringConverter implements Converter<String, String> {

    @Override
    public String convert(String s) {
//同理可以和上面的一样自定义过滤转换规则
        return StringUtils.isEmpty(s) ? s : HtmlUtils.htmlEscape(s);
    }

}

step2:WebMvcConfig中添加如下方法

public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
   resolvers.add(new TokenDataHandlerMethodArgumentResolver());
}

测试

a).测试方法

//测试  @RequestBody 传参过滤(json传参)
@PostMapping("/testXss")
public BaseResponseResult test(@RequestBody User user){
   System.out.println(user.getUserCode());
   return new BaseResponseResult(200,"testXss",user.getUserCode());
}

//测试  @PathVariable 传参过滤(url后面带参数传参)
@GetMapping("/testXss/{openId}")
public BaseResponseResult getBasicUserInfo(@PathVariable(value="openId",required=true) String openId){
   System.out.println(openId);
   return new BaseResponseResult(200,"testXss",openId);
}

//测试  @RequestParam 传参过滤(?传参)
@GetMapping("/testXss1")
public BaseResponseResult test(@RequestParam(value="openId",required=true) String openId){
   System.out.println(openId);
   return new BaseResponseResult(200,"testXss",openId);
}

b).测试结果

补充:

a).sql注入常用语句:(参考:https://blog.csdn.net/u012610902/article/details/80994242)

<script>alert('hello,gaga!');</script> //经典语句,哈哈!

>"'><img src="javascript.:alert('XSS')">

>"'><script>alert('XSS')</script>

<table background='javascript.:alert(([code])'></table>

<object type=text/html data='javascript.:alert(([code]);'></object>

"+alert('XSS')+"

'><script>alert(document.cookie)</script>

='><script>alert(document.cookie)</script>

<script>alert(document.cookie)</script>

<script>alert(vulnerable)</script>

<s&#99;ript>alert('XSS')</script>

<img src="javas&#99;ript:alert('XSS')">

b).过滤器白名单(富文本样式等)

参考(https://www.cnblogs.com/uzxin/p/13476100.html

  • 0
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
回答: 在Spring Boot中实现XSS过滤可以通过创建自定义过滤器来实现。首先,在Spring Boot启动类中添加过滤器扫描的注解`@ServletComponentScan("com.cpic.config.xss")`,这个注解指定了自定义过滤器的位置。同时,还需要在启动类上添加`@WebFilter`注解来指定过滤器的名称和URL模式。具体的过滤逻辑可以在自定义过滤器的`doFilter`方法中实现。\[1\]\[2\] 在过滤器中,可以使用`XssHttpServletRequestWrapper`来对请求进行包装,以实现XSS过滤的功能。这个包装类可以对请求参数进行过滤,包括表单传值(`@RequestParam`)和URL传参(`@PathVariable`)。\[2\] 需要注意的是,Spring Boot中常见的接收参数方式有三种:`@RequestParam`、`@PathVariable`和`@RequestBody`。对于表单传值(`@RequestParam`),可以通过过滤器来进行XSS过滤。\[3\] 综上所述,可以通过创建自定义过滤器来实现Spring Boot中的XSS过滤。在过滤器中对请求进行包装,并对表单传值进行过滤,以防止XSS攻击的发生。 #### 引用[.reference_title] - *1* *2* *3* [Springboot过滤xss](https://blog.csdn.net/Time_Point/article/details/116162806)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值