原创 | 浅谈SpringMvc响应Content-Type与xss

admin 2023年12月27日23:42:54评论22 views字数 5545阅读18分29秒阅读模式
前言
Content-Type 一般用于定义网络文件的类型和网页的编码,决定浏览器将以什么形式、什么编码读取这个文件。
Content-Type 与 XSS 攻击的关系在于,Content-Type 可以指定响应体的 MIME 类型,如果服务器没有正确设置 Content-Type,并且缺少相关的编码/过滤措施,那么恶意js脚本可能会被执行。
下面简单总结了可以触发xss解析的Content-Type:

原创 | 浅谈SpringMvc响应Content-Type与xss

下面简单看看在SpringMVC中,是如何设置Response的Content-Type的。

SpringMVC如何设置Response的Content-Type

以2.7.18版本为例,看看在SpringMVC中是如何设置响应的Content-Type的:

原创 | 浅谈SpringMvc响应Content-Type与xss

在SpringMvc中,DispatcherServlet是前端控制器设计模式的实现,提供Spring Web MVC的集中访问点,而且负责职责的分派。在这里下断点。通过反复调试,最后发现org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodProcessor#writeWithMessageConverters方法里完成Content-Type的配置。

一开始会根据返回值类型进行相应的逻辑处理,若返回值是个org.springframework.core.io.Resource为资源类型的话会进行如下处理:

原创 | 浅谈SpringMvc响应Content-Type与xss

下面就是对contentType的处理了,selectedMediaType表示最终被选中的MediaType,请求是可以接受多种MediaType的,首先会获取请求方指定的contentType,如果请求方法指定了,就以它的为准否则就走else逻辑寻找到一个最为合适的:

原创 | 浅谈SpringMvc响应Content-Type与xss

acceptableTypes是浏览器可以接收的数据类型,也即从请求头里的Accept解析出的内容。首先调用getAcceptableMediaTypes方法会从request 中获取可以接受的媒体类型:

原创 | 浅谈SpringMvc响应Content-Type与xss

查看具体的解析方法,主要是从请求头里的Accept解析获取对应的内容:

原创 | 浅谈SpringMvc响应Content-Type与xss

然后是调用getProducibleMediaTypes方法获得服务器可以输出的数据类型:

原创 | 浅谈SpringMvc响应Content-Type与xss

这里实际上是遍历每个HttpMessageConverter,判断其是否支持写入,如果支持写入的话,便将其支持的mediaType拿到,全部支持写入的HttpMessageConverter对应的mediaType集合就是服务器支持的medisType:

原创 | 浅谈SpringMvc响应Content-Type与xss

默认情况下主要是以下HttpMessageConverter,例如ByteArrayHttpMessageConverter是用来解析byte数组的,StringHttpMessageConverter是用来解析字符串的,MappingJackson2HttpMessageConverter对象是用来转化JSON数据的:

原创 | 浅谈SpringMvc响应Content-Type与xss

常见的HttpMessageConverter以及支持的MediaType类型如下:

原创 | 浅谈SpringMvc响应Content-Type与xss

如果返回的body不为null并且获取不到producibleMediaTypes的话会抛出异常。

接着判断producibleTypes与acceptableTypes是否兼容, 如果兼容, 将更明确的类型添加到mediaTypesToUse中(是producibleTypes和acceptableType的交集):

原创 | 浅谈SpringMvc响应Content-Type与xss

如果最终找不到可用mediaType则按异常处理:

原创 | 浅谈SpringMvc响应Content-Type与xss

然后按照权重排序,遍历mediaTypesToUse并从中确定一个输出的数据类型:

原创 | 浅谈SpringMvc响应Content-Type与xss

到这里已经得到了Http确定的Content-Type输出类型后,需要将请求返回值转化。根据MediaType类型匹配到对应的HttpMessageConverter 消息转换器后处理 body 数据后即可返回:

原创 | 浅谈SpringMvc响应Content-Type与xss

到这里SpringMVC已经完成了Response的Content-Type的设置,响应头的Content-Type主要是由请求头的Accept决定的.

看一个具体的例子映证猜想:

@RequestMapping(value = {"/health"})public String health(String msg) {    return msg;}

默认情况下不传输Accept头部的话,response的Content-Type为text/plain;charset=UTF-8:

原创 | 浅谈SpringMvc响应Content-Type与xss

按照前面的分析,通过调整Accept头部值,即可调整response的Content-Type内容了:

原创 | 浅谈SpringMvc响应Content-Type与xss

潜在XSS风险

@ResponseBody 注解的作用是将Controller的方法返回的对象,通过适当的转换器转换为指定的格式之后,写入到response对象的body,通常用来返回JSON数据或者是XML数据。

例如下面的例子:

@RequestMapping(value = {"/showMsg"})@ResponseBodypublic MsgInfo showMsg(String msg) {    return new MsgInfo(msg);}

正常访问showMsg接口,可以看到将response返回了特定的JSON格式:

原创 | 浅谈SpringMvc响应Content-Type与xss

因为Response的Content-type被设置成了application/json,一般情况下即使msg参数写入了恶意的xss语句,通过浏览器解析后也不会触发:

原创 | 浅谈SpringMvc响应Content-Type与xss

按照前面的猜想,因为Spring应用中响应头的Content-Type主要是由请求头的Accept决定的。那假设Accept头设置为text/html即可触发呢,答案是否定的,可以看到虽然Response的Content-Type虽然成功设置成了text/html,但是却返回了406 status,并没有正常解析请求的返回内容:

原创 | 浅谈SpringMvc响应Content-Type与xss

主要原因如下:

按照前面的解析逻辑,首先调用getAcceptableMediaTypes方法会从request 中获取可以接受的媒体类型,这里是设置好的text/html;charset=UTF-8:

原创 | 浅谈SpringMvc响应Content-Type与xss

然后调用getProducibleMediaTypes方法获得服务器可以输出的数据类型,然后遍历每个HttpMessageConverter,判断其是否支持写入,如果支持写入的话,便将其支持的mediaType拿到,这里会通过MappingJackson2HttpMessageConverter处理,其默认支持的mediaType为application/json:

原创 | 浅谈SpringMvc响应Content-Type与xss

所以最终获取到的支持mediaType如下:

原创 | 浅谈SpringMvc响应Content-Type与xss

然后尝试producibleTypes和acceptableType的交集,找到后更明确的类型添加到mediaTypesToUse中,因为这里text/html明显与application/json没有交集,所以最终mediaTypesToUse集合的size为0,当response的body不为null时,会抛出对应的异常,也就是最终返回的406 status:

原创 | 浅谈SpringMvc响应Content-Type与xss

这里很多研发会有一个误区,对于这种场景,即使没有对用户输入进行处理,也没办法触发xss。

实际上这么理解是并不正确。那么对于常见的返回为JavaBean的场景,是否就不需要考虑xss风险呢?

原创 | 浅谈SpringMvc响应Content-Type与xss

看一个实际的例子,Springboot 默认的json处理方式Jackson,若我们想使用Fastjson进行解析时,需要进行额外的配置,下面是在网上找的一个案例代码:

@Configurationpublic class FastjonConfiguration implements WebMvcConfigurer {

@Override public void configureMessageConverters(List<HttpMessageConverter<?>> converters) { //创建fastJson消息转换器 FastJsonHttpMessageConverter fastConverter = new FastJsonHttpMessageConverter(); List<MediaType> supportedMediaTypes = new ArrayList<>(); supportedMediaTypes.add(MediaType.APPLICATION_JSON); supportedMediaTypes.add(MediaType.APPLICATION_JSON_UTF8); supportedMediaTypes.add(MediaType.APPLICATION_ATOM_XML); supportedMediaTypes.add(MediaType.APPLICATION_FORM_URLENCODED); supportedMediaTypes.add(MediaType.APPLICATION_OCTET_STREAM); supportedMediaTypes.add(MediaType.APPLICATION_PDF); supportedMediaTypes.add(MediaType.APPLICATION_RSS_XML); supportedMediaTypes.add(MediaType.APPLICATION_XHTML_XML); supportedMediaTypes.add(MediaType.APPLICATION_XML); supportedMediaTypes.add(MediaType.IMAGE_GIF); supportedMediaTypes.add(MediaType.IMAGE_JPEG); supportedMediaTypes.add(MediaType.IMAGE_PNG); supportedMediaTypes.add(MediaType.TEXT_EVENT_STREAM); supportedMediaTypes.add(MediaType.TEXT_HTML); supportedMediaTypes.add(MediaType.TEXT_MARKDOWN); supportedMediaTypes.add(MediaType.TEXT_PLAIN); supportedMediaTypes.add(MediaType.TEXT_XML); fastConverter.setSupportedMediaTypes(supportedMediaTypes);
//创建配置类 FastJsonConfig fastJsonConfig = new FastJsonConfig(); //修改配置返回内容的过滤 //WriteNullListAsEmpty :List字段如果为null,输出为[],而非null //WriteNullStringAsEmpty :字符类型字段如果为null,输出为"",而非null //DisableCircularReferenceDetect :消除对同一对象循环引用的问题,默认为false(如果不配置有可能会进入死循环) //WriteNullBooleanAsFalse:Boolean字段如果为null,输出为false,而非null //WriteMapNullValue:是否输出值为null的字段,默认为false fastJsonConfig.setSerializerFeatures( SerializerFeature.DisableCircularReferenceDetect, SerializerFeature.WriteMapNullValue, SerializerFeature.WriteNullListAsEmpty, SerializerFeature.WriteNullStringAsEmpty, SerializerFeature.WriteNullBooleanAsFalse
); fastConverter.setFastJsonConfig(fastJsonConfig); //将fastjson添加到视图消息转换器列表内 converters.add(fastConverter); }}

通过配置@Configuration后,此时Spring会使用Fastjson进行解析。同样是刚刚showMsg接口的例子,当设置Accept后,此时返回response的Content-Type内容为text/html:

原创 | 浅谈SpringMvc响应Content-Type与xss

根据浏览器默认的Accept,也就是说在firefox直接访问是可以触发xss的:

原创 | 浅谈SpringMvc响应Content-Type与xss

原创 | 浅谈SpringMvc响应Content-Type与xss

具体原因其实是因为在配置Fastjson解析时,额外增加了supportedMediaTypes,这些值都会添加到producibleTypes中:

原创 | 浅谈SpringMvc响应Content-Type与xss

然后在尝试producibleTypes和acceptableType的交集,找到后更明确的类型添加到mediaTypesToUse时,可以看到mediaTypesToUse并不像之前那样size为0,而是为text/html:

原创 | 浅谈SpringMvc响应Content-Type与xss

当请求msg为恶意xss语句时,成功触发了xss。

所以在日常审计时,需要额外关注这类场景,避免不必要的风险引入。当然了,用户一切输入都是不可信的,规范用户输入,不依赖框架本身的机制才是最保险的做法。


往期推荐



原创 | 深度剖析GadgetInspector执行逻辑(上)

原创 | 深度剖析GadgetInspector执行逻辑(下)

原创 | 内网安全之隧道代理


原文始发于微信公众号(SecIN技术平台):原创 | 浅谈SpringMvc响应Content-Type与xss

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2023年12月27日23:42:54
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   原创 | 浅谈SpringMvc响应Content-Type与xsshttp://cn-sec.com/archives/2341446.html

发表评论

匿名网友 填写信息