Springboot 防止XSS攻击,包含解决RequestBody 的Json 格式参数

Springboot 防止XSS攻击,包含解决RequestBody 的Json 格式参数

欢迎大家去我的个人网站踩踩 点这里哦

一、前言

最近项目做安全测试,发现存在XSS攻击的可能,于是乎上网找找看,找了很多基本都是继承HttpServletRequestWrapper,对getParam、getQueryString等获取参数的方法进行重写,对参数进行html转义,马上找一个加上试了试,可是发现保存的对象还是没有转义的,后来才想到项目是前后端分离,基本都是@RequestBody注解接收application/json格式参数,通过以上方法是获取不到参数的。

二、网上大多数的解决方案

XssHttpServletRequestWrapper、XssFilter

package com.sino.teamwork.common.extension;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;

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


public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper {

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

    @Override
    public String getQueryString() {
        return StringEscapeUtils.escapeHtml4(super.getQueryString());
    }

    @Override
    public String getParameter(String name) {
        return StringEscapeUtils.escapeHtml4(super.getParameter(name));
    }

    @Override
    public String[] getParameterValues(String name) {
        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;
    }

}
package com.sino.teamwork.filter;

import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;

import org.springframework.stereotype.Component;

import com.sino.teamwork.common.extension.XssHttpServletRequestWrapper;

/**
 * 
 * @ClassName: XssFilter
 * @Description:TODO(防止xss的过滤器)<br>
 * RequsestBody参数通过修改MappingJackson2HttpMessageConverter来过滤
 * @author: guomh
 * @date: 2020年4月07日 下午5:05:51
 * 
 * @Copyright: 2020
 *
 */
@WebFilter(filterName = "xssFilter", urlPatterns = "/*", asyncSupported = true)
@Component
public class XssFilter implements Filter {

    @Override
    public void destroy() {
        // TODO Auto-generated method stub

    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        // TODO Auto-generated method stub
        HttpServletRequest req = (HttpServletRequest) request;
        XssHttpServletRequestWrapper xssRequestWrapper = new XssHttpServletRequestWrapper(req);
        chain.doFilter(xssRequestWrapper, response);
    }

    @Override
    public void init(FilterConfig arg0) throws ServletException {
        // TODO Auto-generated method stub
    }

 
}

这个只能解决下表的前两种Content-Type方式传的参数

Content-Type 传参方式 接收方式
application/x-www-form-urlencoded 表单key-value HttpServletRequest Parameters 获取
multipart/form-data 表单key-value HttpServletRequest Parameters 获取
application/json json格式文本 HttpServletRequest IO流获取

三、RequestBody注解接收json格式参数解决方法

用@RequestBody 注解会使用默认转换器来进行转换,默认转换器初始化过程是这样的,springboot默认会用 MappingJackson2XmlHttpMessageConverter来转换json

看下官网的文档描述

An HttpMessageConverter implementation that can read and write JSON using Jackson’s ObjectMapper. JSON mapping can be customized as needed through the use of Jackson’s provided annotations. When further control is needed, a custom ObjectMapper can be injected through the ObjectMapper property for cases where custom JSON serializers/deserializers need to be provided for specific types. By default this converter supports ( application/json).

现在目标很明确了,就是要把默认的 MappingJackson2XmlHttpMessageConverter 给替换掉,我们自己写,然后在转换json参数后再进行html转义,理所当然的想到如下办法

@Bean
HttpMessageConverter<?> MappingJackson2HttpMessageConverter() {

}

我们看一下SpringMVC配置 WebMvcConfigurationSupport 里面获取 MessageConverter 的方法,如下图

Springboot 防止XSS攻击,包含解决RequestBody 的Json 格式参数

里面有几个重要的方法,我们看下这几个方法的注释:

addDefaultHttpMessageConverters是系统默认的Converters,我们此方法最重要的一部分,MappingJackson2HttpMessageConverter是new出来的对象,所以并没有被spring容器管理,所以这也就说明了我们通过上面@Bean注解是无法替换掉系统默认的

Springboot 防止XSS攻击,包含解决RequestBody 的Json 格式参数

configureMessageConverters 是自定义的MessageConverters,重写此方法,就是自己手动配置,不会采用springboot默认配置

Springboot 防止XSS攻击,包含解决RequestBody 的Json 格式参数

extendMessageConverters的注释,我们看是扩展或修改converters的,因此我们也通过此方法也可以修改系统默认的

Springboot 防止XSS攻击,包含解决RequestBody 的Json 格式参数

因此我们看到通过重写 configureMessageConverters 、extendMessageConverters 两个方法都可以修改系统默认的转换器

方法一:

重写 configureMessageConverters,我们需要把addDefaultHttpMessageConverters里面系统默认的转换器都写一遍,以保证其他的转化器有效,我们可以把 addDefaultHttpMessageConverters 源码复制出来,在 new MappingJackson2HttpMessageConverter 那里,我们可以 new 一个自定义的MappingJackson2HttpMessageConverter,但是我不建议用此方法,因为addDefaultHttpMessageConverters里面的内容很多,还有一些私有变量,复制出来有些不方便,还容易出错。

方法二:

重写extendMessageConverters,此方法注释说就是让来修改已经配置好的转化器列表呢,我们只需要遍历列表,找到MappingJackson2HttpMessageConverter,我们可以根据类型来判断哪个是 MappingJackson2HttpMessageConverter ,然后移除(注意遍历移除一定要用迭代器),把自定义的添加进去就好了,我们写在 WebMvcConfig 里


@Configuration
public class WebMvcConfig  extends WebMvcConfigurationSupport {

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("swagger-ui.html")
                .addResourceLocations("classpath:/META-INF/resources/");

        registry.addResourceHandler("/webjars/**")
                .addResourceLocations("classpath:/META-INF/resources/webjars/");
    }

    
    @Override
    protected void extendMessageConverters(List<HttpMessageConverter<?>> messageConverters) {
        /**
         * 替换默认的MappingJackson2HttpMessageConverter,过滤(json请求参数)xss
         */
    	ListIterator<HttpMessageConverter<?>> listIterator = messageConverters.listIterator();
    	while(listIterator.hasNext()) {
    		HttpMessageConverter<?> next = listIterator.next();
    		if(next instanceof MappingJackson2HttpMessageConverter) {
    			listIterator.remove();
    			break;
    		}
    	}
        messageConverters.add(getMappingJackson2HttpMessageConverter());

    }
    
    public MappingJackson2HttpMessageConverter getMappingJackson2HttpMessageConverter() {
        // 创建自定义ObjectMapper
        SimpleModule module = new SimpleModule();
        module.addDeserializer(String.class, new JsonHtmlXssDeserializer(String.class));
        ObjectMapper objectMapper = Jackson2ObjectMapperBuilder.json().applicationContext(this.getApplicationContext()).build();
        objectMapper.registerModule(module);
        // 创建自定义消息转换器
        MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter = new MappingJackson2HttpMessageConverter();
        mappingJackson2HttpMessageConverter.setObjectMapper(objectMapper);
        //设置中文编码格式
        List<MediaType> list = new ArrayList<>();
        list.add(MediaType.APPLICATION_JSON_UTF8);
        mappingJackson2HttpMessageConverter.setSupportedMediaTypes(list);
        return mappingJackson2HttpMessageConverter;
    }

}

/**
 * 对入参的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;
    }
}

我们看到最重要的代码其实是ObjectMapper 里面的 JsonHtmlXssDeserializer,这个解析器是解析json字符串时调用的,我们在里面对解析出来的参数进行转义就可以了。

方法三(不行):

网上还有一个方法是替换默认的ObjectMapper的,从第二种方法我们可以看出来,其实最终是为了替换默认的ObjectMapper,于是乎网上有了这种写法

 /**
 * 过滤json类型的
 * @param builder
 * @return
 */
 @Bean
 @Primary
 public ObjectMapper xssObjectMapper(Jackson2ObjectMapperBuilder builder) {
 //解析器
 ObjectMapper objectMapper = builder.createXmlMapper(false).build();
 //注册xss解析器
 SimpleModule xssModule = new SimpleModule("XssStringJsonSerializer");
 xssModule.addSerializer(new XssStringJsonSerializer());
 objectMapper.registerModule(xssModule);
 //返回
 return objectMapper;
 }

也是想用 @bean 注解来替换默认的ObjectMapper,这样真的可以吗,这样其实跟用@Bean注解替换 MappingJackson2XmlHttpMessageConverter 是一样的,我们看下源码

Springboot 防止XSS攻击,包含解决RequestBody 的Json 格式参数

默认是用Jackson2ObjectMapperBuilder来构造ObjectMapper的,我们进去build方法看一下,可以看到也是new出来的,并没有被spring容器管理,所以这种方法不可以

Springboot 防止XSS攻击,包含解决RequestBody 的Json 格式参数

四、总结

还是那句话,网上很多的文章代码估计不知道测过没有,拿来用很多都不适用,我们可以拿来参考,找到其中的思路,再自己分析原理,理解透了这样子才能真正解决自己的问题。最后欢迎大家到我的个人网站看看,点这里

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/111314.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • idea 2021激活码_最新在线免费激活

    (idea 2021激活码)本文适用于JetBrains家族所有ide,包括IntelliJidea,phpstorm,webstorm,pycharm,datagrip等。https://javaforall.net/100143.htmlIntelliJ2021最新激活注册码,破解教程可免费永久激活,亲测有效,上面是详细链接哦~0…

    2022年3月28日
    102
  • 怎么创建数据表_sqlserver创建表的sql语句

    怎么创建数据表_sqlserver创建表的sql语句知识点:数据库表的相关概念、创建数据库表的方法、设计数据库表、向数据库表中插入数据、建立不同数据库表之间的关系、删除数据库表。1、数据表相关的一些概念1.1数据库里的数据是如何保存的?数据库到底是怎么存储数据的?比如要把学生信息存储到数据库里,能把学生塞进数据库吗?肯定是把学生的数据信息抽象出来,把一些重要信息以文字或数字的形式保存到数据库中去。…

    2025年7月30日
    3
  • 二、八、十、十六进制转换(图解篇)「建议收藏」

    二、八、十、十六进制转换(图解篇)「建议收藏」一.本文所涉及的内容(Contents)本文所涉及的内容(Contents)背景(Contexts)进制转换算法(Convert)(二、八、十六进制)→(十进制)二进制→十进制八进制→十

    2022年8月6日
    5
  • bWAPP 安装_bud在哪里下载

    bWAPP 安装_bud在哪里下载1.前言bwapp是一款非常好的漏洞演示平台,其包含有100多个漏洞。bwapp漏洞平台的安装大致有3种单独下载,部署到apache+mysql+php环境下直接下载虚拟机使用docker进行安装2.单独下载先去下载bwapp环境然后将下载的压缩包解压在bwapp/admin下找到settings.php文件将配置文件的内容修改为自己的内容然后访问bwapp进…

    2022年9月24日
    3
  • 【SpringBoot】34、SpringBoot整合Redis实现序列化存储Java对象

    【SpringBoot】34、SpringBoot整合Redis实现序列化存储Java对象前面我们已经介绍过【SpringBoot】十七、SpringBoot中整合Redis,我们可以看出,在SpringBoot对Redis做了一系列的自动装配,使用还是非常方便的一、背景1、思考‘通过我们前面的学习,我们已经可以往Redis中存入字符串,那么我们要往Redis中存入Java对象该怎么办呢?2、方案我们可以将Java对象转化为JSON对象,然后转为JSON字符串,存入Redis,那么我们从Redis中取出该数据的时候,我们也只能取出字符串,并转

    2022年6月21日
    33
  • linux启动网络服务步骤_centos7启动网络服务命令

    linux启动网络服务步骤_centos7启动网络服务命令linux系统下重启网络服务的两种方法发布时间:2020-04-0211:25:25来源:亿速云阅读:207作者:小新今天小编给大家分享的是linux系统下重启网络服务的两种方法,很多人都不太了解,今天小编为了让大家更加了解linux系统下重启网络服务的方法,所以给大家总结了以下内容,一起往下看吧。一定会有所收获的哦。Linux启动、关闭、重启网络服务的两种方式:1、使用service脚本来调…

    2022年4月19日
    113

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号