浅谈Spring actuator env endpoint的一个误区

admin 2023年7月12日01:46:47浅谈Spring actuator env endpoint的一个误区已关闭评论71 views字数 5746阅读19分9秒阅读模式

  Spring Boot Actuator 是 Spring Boot 提供的一个管理和监控 Spring Boot 应用程序的插件。Actuator 可以提供有关应用程序的健康状况、度量、日志记录和其他信息,可以通过 HTTP 或 JMX 等不同方式进行访问。

  /env 是 Spring Boot Actuator 的一个端点,用于显示当前应用程序的环境属性。该端点返回一个 JSON 格式的响应,其中包含有关应用程序环境的详细信息,例如操作系统、Java 运行时环境、应用程序配置属性等等。

浅谈Spring actuator env endpoint的一个误区

一、关于env端点的一个误区

  默认情况下,actuator的env端点是不支持POST请求修改操作的。但是可通过配置开启env端点的post请求,参考https://spring.io/blog/2020/03/05/spring-cloud-hoxton-service-release-3-sr3-is-available

浅谈Spring actuator env endpoint的一个误区

PS:actuator 1.x只需要引入对应的springcloud即可默认开启了。

  在引入springcloud后,会依赖一个组件叫 SnakeYAML 其可以触发yaml反序列化,利用actuator的/env POST 功能可以将spring.cloud.bootstrap.location属性的值为恶意yml配置文件的地址,使用/refresh接口刷新后即可触发远程加载yaml 实现urlclassloader达到RCE的效果。

  针对SpringBoot-Actuator-SnakeYAML-RCE的情况,很多时候会选择关闭掉env端点来规避风险

  在actuator 1.x,/env端点可以由如下属性进行关闭:

endpoints.env.enabled=false

  在spring boot 2.0以后,actuator默认只开启了info和health端点,要想使用其他的端点,需要在application.yml相关配置文件中打开,并且所有的端点都以默认的路径/actuator/开始,如果需要独立关闭/env端点的话可以通过如下属性进行操作:

management.endpoint.env.enabled=false

  这里存在一个误区,在actuator 1.x中,很多时候大家都认为endpoints.env.enabled=false后就env POST端点就没有暴露出去了,规避了风险,实际上简单的通过配置关闭env端点并不能解决问题。

二、具体原理

针对前面的情况,看一下具体的原理(在spring boot 2.0以后,actuator默认只开启了info和health端点,这里只讨论1.x的情况):

2.1、env端点的注册

  以spring-boot-actuator-1.5.1.RELEASE版本为例:

  首先通过actuator访问/mappings查看env端点具体的方法调用,可以看到是通过endpointHandlerMapping进行处理的:

浅谈Spring actuator env endpoint的一个误区

浅谈Spring actuator env endpoint的一个误区

  访问/beans节点,查看处理类endpointHandlerMapping的具体实现:

  可以看到endpointHandlerMapping对应的类为org.springframework.boot.actuate.endpoint.mvc.EndpointHandlerMapping,初始化的configuration类为org/springframework/boot/actuate/autoconfigure/EndpointWebMvcManagementContextConfiguration.class

浅谈Spring actuator env endpoint的一个误区

  首先查看EndpointWebMvcManagementContextConfiguration的具体实现,可以看到其会通过调用endpointHandlerMapping方法来处理,这里首先会获取容器中的MvcEndpoint接口实现类,然后实例化EndpointHandlerMapping:

浅谈Spring actuator env endpoint的一个误区

  MvcEndpoints初始化代码如下:

  • 查找spring的beanfactory中所有的MvcEndpoint类和Endpoint类
  • 如果某个MvcEndpoint的getEndpointType方法的返回结果和某个Endpoint的class相冲突,则选取MvcEndpoin
  • 最后将查找选取的结果放在MvcEndpoints的endpoints方法中并返回

浅谈Spring actuator env endpoint的一个误区

  通过断点调试,可以看到当前环境endpoints 有21个:

浅谈Spring actuator env endpoint的一个误区

  继续调试,后面会调用对应Endpoint的构造方法来实现映射(EnvironmentMvcEndpoint也在21个endpoints里):

浅谈Spring actuator env endpoint的一个误区

  以EnvironmentMvcEndpoint为例,查看具体的实现:

org.springframework.boot.actuate.endpoint.mvc.EnvironmentMvcEndpoint#value这个方法实际上就是/env/{name:.*}这个路由的具体调用:

浅谈Spring actuator env endpoint的一个误区

浅谈Spring actuator env endpoint的一个误区

  其中@ActuatorGetMapping 就是@RequestMapping的封装,被该注解标注的方法,其请求方式为get,产生的数据格式为application/vnd.spring-boot.actuator.v1+json和application/json,具体定义如下:

浅谈Spring actuator env endpoint的一个误区

  查看EnvironmentMvcEndpoint的构造方法,直接调用直接父类构造函数(EnvironmentMvcEndpoint的父类是EndpointMvcAdapter):

浅谈Spring actuator env endpoint的一个误区

  在EndpointMvcAdapter中,org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter#invoke实际上就是/env路由的具体调用:

浅谈Spring actuator env endpoint的一个误区

浅谈Spring actuator env endpoint的一个误区

  EndpointMvcAdapter,它代理了MvcEndpoint,实现其invoke方法。这里的delegate是EnvironmentEndpoint,所以实际调用时,最终调用的是org.springframework.boot.actuate.endpoint.EnvironmentEndpoint#invoke:

浅谈Spring actuator env endpoint的一个误区

  到这里大概可以知道EnvironmentMvcEndpoint实际上就是把EnvironmentEndpoint通过HTTP服务暴露,然后通过/env和/env/{name:.*}两个请求路由进行访问。

2.2、env端点生效

  前面提到会调用对应Endpoint的构造方法来实现映射,对应的方法里有一个注解@ConditionalOnEnabledEndpoint:

浅谈Spring actuator env endpoint的一个误区

浅谈Spring actuator env endpoint的一个误区

  该注解会通过org.springframework.boot.actuate.condition.OnEnabledEndpointCondition类进行处理。

  首先是getMatchOutcome方法:

浅谈Spring actuator env endpoint的一个误区

  主要做了如下处理:

  • 获得@ConditionalOnEnabledEndpoint配置的Endpoint名字和enabledByDefaultenabled的值
  • 调用determineEndpointOutcome方法,获得endpoints.{name}.enabled的配置并返回,例如env端点的话会读取endpoints.env.enabled的值

浅谈Spring actuator env endpoint的一个误区

  • 读取endpoints.enabled的配置,如果没有配置的话,则默认匹配

浅谈Spring actuator env endpoint的一个误区

  也就是说@ConditionalOnEnabledEndpoint注解满足如下条件时或生效:

  • endpoints. env.enabled=true
  • endpoints.enabled=true
  • 未配置endpoints.enabled,endpoints.env.enabled时默认生效

  同样是之前的环境,在application.properties进行如下配置关闭env端点:

endpoints.env.enabled=false

  通过断点调试,可以看到当前环境endpoints 从21个减少到了20个,EnvironmentMvcEndpoint没有了:

浅谈Spring actuator env endpoint的一个误区

2.3、env如何支持POST

  根据前面的分析,通过@ActuatorGetMapping注解仅仅实现了GET请求的调用,并未实现POST。看看具体的env POST是如何实现的。

  同样的通过actuator访问/mappings查看env端点具体的方法调用,可以看到是通过org.springframework.cloud.context.environment.EnvironmentManagerMvcEndpoint#value处理的,查看具体的实现:

浅谈Spring actuator env endpoint的一个误区

  以spring-cloud-context-1.1.3为例:

  org.springframework.cloud.context.environment.EnvironmentManagerMvcEndpoint#value方法通过@RequestMapping标记并且限制了只能通过POST请求:

浅谈Spring actuator env endpoint的一个误区

  同时EnvironmentManagerMvcEndpoint实现了MvcEndpoint接口,在MvcEndpoints初始化时也能获取到:

浅谈Spring actuator env endpoint的一个误区

  EnvironmentManagerMvcEndpoint是在org.springframework.cloud.autoconfigure.LifecycleMvcEndpointAutoConfiguration中注入,这里是通过@ConditionalOnProperty注解来判断的(@ConditionalOnProperty可以通过配置文件中的属性值来判定是否被注入,这里是endpoints.env.post.enabled,matchIfMissing属性为true时,配置文件中缺少对应的value或name的对应的属性值,也会注入成功):

浅谈Spring actuator env endpoint的一个误区

  到这里其实已经大概知道具体的原因了。实际上env POST和env GET在endpointHandlerMapping处理时并不是同一个endpoint。所以当通过endpoints.env.enabled=false关闭env GET时,env POST 端点(EnvironmentManagerMvcEndpoint)仍因为默认配置注入到环境中。
  根据前面的分析,这里尝试在application.properties中进行如下配置:

endpoints.env.post.enabled=false

浅谈Spring actuator env endpoint的一个误区

  但是env GET可以正常访问,验证了前面的猜想:

浅谈Spring actuator env endpoint的一个误区

  查看调试信息,EnvironmentManagerMvcEndpoint没有注入,但是代表env GET实现的EnvironmentMvcEndpoint注入成功并且获取到了,所以env GET可以访问:

浅谈Spring actuator env endpoint的一个误区

三、其他

  对于actuator 2.x生态,设计上会有区别。

  以spring-cloud-context-2.2.1.RELEASE为例,其env POST的实现主要在WritableEnvironmentEndpointAutoConfiguration:

  这里不再是EnvironmentManagerMvcEndpoint,而是WritableEnvironmentEndpoint:

浅谈Spring actuator env endpoint的一个误区

  而POST的实现主要是通过@EndpointWebExtension注解扩展现有的Endpoint(ReadOperation、@WriteOperation和@DeleteOperation每个注解的方法都有对应操作对象,根据操作对象、Endpoint对象、Endpoint名称在RequestMappingInfo进行处理,然后对外提供服务):

浅谈Spring actuator env endpoint的一个误区

  那么在2.x中,实际上只需要关闭env端点,即可禁用env POST了,例如如下配置,首先暴露endpoint,然后禁用env:

management.endpoints.web.exposure.include=*
management.endpoint.env.enabled=false

  此时再次尝试请求env POST,返回404 status:

浅谈Spring actuator env endpoint的一个误区

四、修复措施

  根据前面的分析,env端点POST支持主要是通过spring-cloud-context组件控制的。在1.x中需要额外的注意。

  当引入了spring-cloud-starter-config或spring-cloud-context 大于等于 2.2.2.RELEASE时:

<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-context</artifactId>
<version>2.2.2.RELEASE</version>
</dependency>

  若未配置management.endpoint.env.post.enabled=true,则不支持post请求,此时无影响。

  但当版本为2.2.1.RELEASE及以下时候,则可以直接请求POST:

<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-context</artifactId>
<version>2.2.1.RELEASE</version>
</dependency>

  此时如果需要关闭,通过配置对应的属性进行处理:

  • actuator 1.x

endpoints.env.post.enabled=false

  • actuator 2.x

management.endpoint.env.post.enabled=false

  同时关闭env POST和GET后,访问会返回404,达到预期的效果,从一定程度规避了env POST端点的风险:

浅谈Spring actuator env endpoint的一个误区

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2023年7月12日01:46:47
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   浅谈Spring actuator env endpoint的一个误区https://cn-sec.com/archives/1868849.html