Spring Boot Actuator 暴露出来的信息远比表面看到的要多。很多人以为关掉 env、beans 端点就万事大吉,实际上 heapdump、threaddump、mappings 这些看似无害的端点,在攻击者手里就是完整的应用架构图和运行时数据快照。真正的风险不在于端点本身,而在于你根本不知道暴露出去的数据能组合出什么攻击路径。
heapdump 是最大的信息泄漏源heapdump 端点一旦暴露,攻击者下载到的 .hprof 文件里包含 JVM 堆中所有对象实例。这意味着什么?数据库连接字符串、Redis 密码、第三方 API 密钥、用户会话令牌、请求参数中的敏感数据,全部以明文或接近明文的形式躺在里面。用 VisualVM 或 Eclipse Memory Analyzer 打开 dump 文件,搜索 javax.crypto.spec.SecretKeySpec 就能直接定位加密密钥,搜索 org.springframework.security.core.userdetails.User 就能拿到内存中的用户名和密码。这不是理论攻击,实际渗透测试中通过 heapdump 拿到生产凭证的案例比比皆是。如果你的应用曾经在请求参数里传递过身份证号、银行卡号,这些数据只要没被 GC 回收,就完整保留在 dump 文件中。
threaddump 暴露业务逻辑和调用链threaddump 端点输出所有线程的完整堆栈跟踪。攻击者通过分析线程名称和堆栈信息,能反推出你的定时任务逻辑、消息队列消费逻辑、甚至数据库操作的具体 SQL 语句。如果某个线程卡在 executeQuery 方法上,堆栈里可能直接显示完整的 SQL 和参数值。结合 mappings 端点暴露的 API 路径和请求映射关系,攻击者可以精确绘制出整个应用的接口清单、参数结构、权限要求。这三者组合起来,就是一份完整的内网渗透地图。
env 端点的属性过滤是自欺欺人很多人知道要配置 management.endpoint.env.post.enabled=false 或者设置 keys-to-sanitize,但 sanitize 只是在前端展示时做替换,实际请求 env 端点时后端返回的完整 JSON 里,敏感值依然存在,只不过被替换成了星号。问题在于 Spring Boot 的属性覆盖机制——如果你通过 POST 请求向 /actuator/env 发送属性更新,某些情况下可以绕过 sanitize 直接读取原始值。更隐蔽的是,env 端点会暴露所有配置属性来源,包括命令行参数、系统属性、环境变量。攻击者通过分析这些信息,可以推断出部署环境、内部域名、服务依赖关系。
gateway 和 refresh 端点的远程代码执行风险如果应用集成了 Spring Cloud Gateway 并且暴露了 gateway 端点,攻击者可以通过 POST 请求动态添加恶意路由,指向内部未授权的微服务接口,或者直接注入 SpEL 表达式实现远程代码执行。refresh 端点配合 spring-cloud-starter-bus-amqp 使用时,攻击者可以伪造 RefreshRemoteApplicationEvent 事件,触发整个微服务集群的配置刷新,将恶意配置注入到所有节点。这不是漏洞,这是分布式配置中心被误暴露后的必然结果。
第一层防护:网络隔离与访问控制Actuator 端口应该绑定到独立的内网地址,不与业务端口共用。在 application.yml 中配置 management.server.port=8081 和 management.server.address=127.0.0.1,让 Actuator 只监听本地回环地址。外部访问通过反向代理严格控制,Nginx 层面直接拒绝 /actuator 路径的外部请求。如果必须对外暴露部分端点用于健康检查,单独开放 health 端点并配置 management.endpoints.web.exposure.include=health,其余全部设为 exclude。health 端点也要禁用详细信息展示:management.endpoint.health.show-details=never。
第二层防护:认证与授权给 Actuator 单独配置安全拦截器,不要复用业务接口的认证体系。引入 spring-boot-starter-security,专门为 Actuator 路径设置独立的用户和角色:
@Configuration
public class ActuatorSecurityConfig {
@Bean
public SecurityFilterChain actuatorFilterChain(HttpSecurity http) throws Exception {
http.securityMatcher(EndpointRequest.toAnyEndpoint())
.authorizeHttpRequests(auth -> auth
.requestMatchers(EndpointRequest.to("health")).permitAll()
.anyRequest().hasRole("ACTUATOR_ADMIN")
)
.httpBasic(Customizer.withDefaults());
return http.build();
}
}
这里的关键是 securityMatcher 只作用于 Actuator 端点,不影响业务接口。角色名称不要用常见的 ADMIN,换成项目特定的名称降低被猜测的概率。如果使用 Spring Boot 3.x,HttpSecurity 配置方式已经更新,注意 lambda DSL 的写法变化。
第三层防护:端点精细化控制逐个审视每个端点的必要性。heapdump 在生产环境必须关闭:management.endpoint.heapdump.enabled=false。env 端点如果无法关闭,至少禁用 POST 操作:management.endpoint.env.post.enabled=false。threaddump 和 dump 端点同样默认关闭。mappings 端点建议关闭,如果开发阶段需要,通过条件配置只在非生产 profile 中启用。loggers 端点允许动态修改日志级别,攻击者可以利用它将敏感包的日志级别调到 TRACE,从而在日志中获取详细数据,生产环境也应关闭或限制为只读。
第四层防护:自定义端点过滤器即使配置了认证,某些端点返回的数据量仍然过大。可以注册自定义的 OperationResponseBody 拦截器,在响应返回前对敏感字段做真正的脱敏处理:
@WebEndpointExtension(endpoint = EnvEndpoint.class)
public class SanitizedEnvEndpoint {
private final EnvEndpoint delegate;
public SanitizedEnvEndpoint(EnvEndpoint delegate) {
this.delegate = delegate;
}
@ReadOperation
public EnvironmentDescriptor environment() {
EnvironmentDescriptor descriptor = delegate.environment();
descriptor.getPropertySources().forEach(source ->
source.getProperties().forEach((k, v) -> {
if (k.contains("password") || k.contains("secret")) {
v.setValue("*REDACTED*");
}
})
);
return descriptor;
}
}
这种方式在数据源头做脱敏,而不是依赖前端的 sanitize 规则。对于 heapdump 这类无法在应用层拦截的端点,只能通过禁用或严格的网络隔离来防护。
第五层防护:运行时监控与告警Actuator 的 audit 端点可以记录所有敏感操作,结合 micrometer 指标暴露到 Prometheus,设置告警规则。当 heapdump 端点被访问时,Spring Boot 会发布 AuditEvent,类型是 ENDPOINT_ACCESS。通过实现 AuditEventRepository 将这些事件持久化或推送到 SIEM 系统。同时监控 Actuator 端点的访问频率,正常健康检查的间隔是固定的,如果出现突发的大量请求或者非预期的端点路径访问,立即触发告警。在日志中记录所有对 Actuator 的非 health 端点访问,包括来源 IP、请求端点、时间戳。
Spring Boot 3.x 的变更与注意事项Spring Boot 3.x 中 Actuator 的默认路径仍然是 /actuator,但配置属性前缀从 management.endpoints.web 统一为 management.endpoints.web。health 端点增加了 health groups 功能,可以将多个健康指标分组,对外只暴露聚合结果。info 端点支持从 Git 提交信息自动生成版本信息,生产环境注意不要暴露内部 Git 仓库地址和分支名称。prometheus 端点成为独立模块,需要单独引入 micrometer-registry-prometheus 依赖。这些变更不影响安全配置的基本逻辑,但在升级时需要重新验证端点暴露情况。
云原生环境下的特殊风险在 Kubernetes 环境中,Pod 的存活探针和就绪探针通常配置为访问 /actuator/health,这意味着 health 端点必须对 kubelet 开放。但很多团队图省事,直接把所有 Actuator 端点都暴露在同一个端口上,然后通过 Service 对外暴露。正确的做法是让探针访问业务端口上的 /actuator/health,而 Actuator 的管理端口只绑定 localhost,通过 kubectl port-forward 临时访问。如果使用 Istio 或 Linkerd 等服务网格,在 VirtualService 或 ServiceProfile 中明确拒绝 /actuator 路径的外部流量,只允许来自监控系统和集群内部组件的请求。
代码层面的信息泄漏防范Actuator 暴露的问题本质上是应用内部信息的外泄。除了配置层面的防护,代码中也要注意不要在异常信息、日志输出、响应体中包含敏感数据。自定义的 Actuator 端点要经过安全审查,确保返回的数据不包含密码、令牌、内部 IP 等。使用 @Endpoint 注解自定义端点时,默认就是暴露的,需要显式设置 @Endpoint(id = "custom", enableByDefault = false) 来控制默认状态。info 端点中自定义的构建信息、Git 信息要过滤掉敏感的环境标识和内部路径。
定期审计与渗透测试每季度对生产环境的 Actuator 端点进行一次全面审计,使用 nmap 扫描管理端口,用 curl 逐端点测试访问权限。编写自动化脚本模拟攻击者视角:先尝试无认证访问 /actuator,再尝试常见弱口令,然后逐个探测端点。将审计结果纳入安全基线的检查项。对于发现的暴露端点,不要简单地在反向代理上加路径拦截就完事,要从应用配置层面彻底关闭,因为反向代理配置可能被误修改或绕过。保留最小权限原则,每新增一个微服务就默认关闭所有 Actuator 端点,需要时按白名单方式逐个开启。
