☰
Spring Boot Actuator未授权访问:高频暴露面排查与安全加固
2026/9/29 7:35:16 网站建设 项目流程

前阵子一个做企业内部系统的朋友来找我,说他们刚上线两周的运营后台被甲方安全扫描打了一堆高危,报告里排第一的就是"Spring Boot Actuator 未授权访问",附件还贴了/actuator/env、/actuator/heapdump的直接响应截图,连状态码 200 都标出来了。他当时的表情挺懵的——代码是自己写的,鉴权框架也接了,怎么就成了高危。我打开他们的application.yml看了三分钟就找到原因了:management.endpoints.web.exposure.include: "*",一行为了调试方便留下的配置,把整套运维端点直接摆到了公网入口。这件事让我觉得有必要把 Spring Boot 这类"未授权访问"问题系统讲一遍,因为它的坑不在框架本身,而在于默认约定、开发习惯和部署姿态这三件事叠在一起。这篇内容我想聊的就是:Spring Boot 项目里常见的未授权访问面到底有哪些、它们为什么会出现、怎么自己查出来、以及怎么用配置和代码把它收干净。不管你是刚接触 Spring Boot 的开发者,还是负责安全加固和应急的运维同学,都能从里面找到能直接抄的配置和排查思路。

1. 这个漏洞到底漏在哪:Spring Boot 的"默认约定"与暴露面的由来

1.1 一个真实场景:上线第二天就被扫出来

回到我那位朋友的案例。他们的技术栈是 Spring Boot 2.7 加 Spring Security,接口层做了 JWT 校验,这部分没问题。问题出在两个地方:一是pom.xml里引入了spring-boot-starter-actuator却没有做任何暴露限制;二是配置文件里为了本地调试方便,开了management.endpoints.web.exposure.include: "*",并且没设置独立的管理端口。部署到云主机后,Actuator 的端点跟着业务端口一起对外了。

扫描器其实不会写什么高级规则,它只是拿一份常见路径字典去撞。/actuator、/actuator/env、/actuator/health、/actuator/beans、/actuator/heapdump这些路径挨个请求一遍,谁返回 200 并且响应体里有特征字符串,就记一条。他们那次之所以被标成高危,是因为/actuator/heapdump返回了一个几十兆的二进制文件——内存快照里可能包含运行时的配置属性、连接串、令牌片段。这就是"未授权访问"最典型的样子:接口本身没漏洞,但入口没锁,等于把内部资料室的钥匙挂在了大门上。

1.2 便利性与安全性的账,Spring Boot 帮你怎么算的

Spring Boot 的设计哲学是"约定优于配置",这句话在提升开发效率上非常成功,但它在安全上有个隐含前提:默认假设你部署在受信任的内网环境。Actuator 就是一个典型例子。它在 1.x 时代默认把所有端点都通过 HTTP 暴露出去,因为那时候它的定位是"内网运维工具"。到了 2.x,官方把默认暴露范围收窄到只剩health和info,但保留了include: "*"这个开关,也保留了对/env、/heapdump这类敏感端点的支持。

这个设计的逻辑是:把选择权交给开发者。问题是很多开发者根本不知道有这道选择题。项目脚手架生成什么样就用什么样,教程里写include: "*"就跟着抄,本地跑通就行,没人会去想上线之后这些路径会变成什么样。所以我一直跟团队里的人说,Spring Boot 的安全问题,八成不是"写错了",而是"没想过"。你要做的第一件事,是把项目里所有跟"暴露""端口""监控""控制台"相关的配置项列出来,逐个问自己一句:这东西上线以后,谁能访问到?

从影响范围看,这类问题的波及面比想象中大。它不是某一个业务接口的问题,而是一整类"旁路入口"的问题。业务接口的鉴权你可能做得很扎实,但旁路入口走的是另一套逻辑,它们往往不经过你的 JWT 过滤器,也不在你的接口权限表里。一旦被扫到,轻则泄露接口清单、配置项、依赖版本,重则泄露内存中的敏感数据。而扫描这类问题是全自动的、批量的、成本极低的,这就意味着你只要暴露了,被发现的概率几乎是 100%。

2. 六大高频暴露面盘点:按危害等级排个序

2.1 Actuator:最典型的"运维后门变前台入口"

Actuator 是 Spring Boot 官方提供的生产级监控模块,端点数量很多,我按敏感程度分三档说。

第一档是信息泄露类,包括/env(环境变量与配置属性)、/configprops(配置 Bean 的属性)、/beans(容器里所有 Bean 定义)、/mappings(全部请求路由映射)、/heapdump(堆内存快照)、/threaddump(线程栈)、/logfile(日志文件)。这里面/mappings的价值在于它能直接把你的全部接口路径吐出来,包括那些没写进文档的内部接口;/heapdump更狠,它是一个二进制文件,里面可能有数据库连接串、第三方密钥、用户会话对象。

第二档是可操作类,/loggers能动态修改日志级别,/shutdown能直接关闭应用(2.x 默认不通过 Web 暴露,但可以手动开),/jolokia是 JMX over HTTP 的桥,能读甚至能操作 MBean,这条链在历史上有过被用来做远程代码执行的记录。第三档是弱信息类,/health、/info、/metrics、/caches,单独看危害不大,但/metrics会暴露内部指标名,/health的show-details打开后会暴露数据库、磁盘、中间件的健康状态和版本信息,这些都能作为下一步探测的情报。

要注意的一个细节是:/env在 2.0 之后对形如password、secret、key的属性名做了脱敏,显示成******。但这不代表安全,因为脱敏只是展示层的处理,/heapdump拿到的是内存原始数据,脱敏在这里完全无效。所以"我看了/env都是星号,应该没事"这个判断是错的。

2.2 API 文档页:Swagger 与 Knife4j 把接口清单白送

Swagger 是接口文档的事实标准,springfox和springdoc-openapi两个生态都在用。问题在于这些文档页默认是不需要认证的,因为它们是"给开发者看的"。常见的暴露路径包括/swagger-ui.html、/swagger-ui/index.html、/v2/api-docs、/v3/api-docs、/v3/api-docs/swagger-config,如果用 Knife4j 的话还有/doc.html和/v2/api-docs?group=xxx这种分组的接口。

这些页面的危害不在于"能调用接口",而在于它把接口的完整结构、参数名、参数类型、示例值、甚至注解里写的注释全部暴露出来。攻击者拿到这份清单,等于拿到了你的系统地图,接下来所有的接口探测都不用猜了。我在做应急的时候见过一个案例,某系统的/v3/api-docs没关,里面有个内部接口叫/internal/user/exportAll,参数只有page和size,返回全量用户数据。这个接口在业务侧是有权限控制的,但接口名和参数结构被文档页泄露后,攻击者直接把请求打过去,发现开发环境的一个配置疏忽让这个接口的鉴权被跳过了。

文档页的另一个麻烦是它常常被"随手开着忘记关"。很多团队在上线前会关掉,但灰度环境、预发环境、内部测试域名上还留着,而这些环境的域名有时候和主域名只差一个前缀,很容易被枚举到。

2.3 数据源监控台:Druid 与 H2 Console

如果项目里用了阿里 Druid 连接池并且引入了druid-spring-boot-starter,会自带一个监控页面,路径通常是/druid/index.html、/druid/sql.html、/druid/websession.html。这个页面的默认状态在早期版本里是不需要登录的,后来加了login-username和login-password配置,但很多项目要么没配,要么配了默认的弱口令。

Druid 监控页能干什么?它能看实时 SQL 执行记录、慢 SQL、URI 监控、Session 监控。也就是说,你系统在跑什么 SQL、哪个接口在调用、Session 里存了什么,全都看得到。这个信息量比一般的接口泄露大得多,因为它直接指向了数据层。SQL 监控页里往往能看到完整的表名和字段名,配合/actuator/mappings拿到的接口清单,攻击者可以对你的数据模型做相当精确的推断。

H2 Console 是另一个高频问题。H2 是内存数据库,很多人拿它做单元测试和本地开发,配置项是spring.h2.console.enabled=true,路径是/h2-console。这个开关如果被带到了生产配置里,并且spring.h2.console.settings.web-allow-others被设成了true,就会允许远程连接数据库控制台。虽然生产环境一般不会用 H2 存业务数据,但这个控制台存在本身就是一个信息泄露点,而且它经常和"开发配置被误提交"这类问题捆绑出现。

2.4 集成中间件的连带风险:Redis、MongoDB、Nacos

这一块严格说不算 Spring Boot 框架本身的漏洞,但它在"Spring Boot 项目被扫出未授权访问"的场景里出现的频率非常高,因为 Spring Boot 集成这些中间件太方便了。

Redis 在 Spring Boot 里通过spring-boot-starter-data-redis集成,配置项是spring.redis.host和spring.redis.port。如果这台 Redis 部署时没设密码,并且监听在0.0.0.0,那么它就是未授权可访问的。同样的问题在 MongoDB 上更常见,早年 MongoDB 默认不开认证,spring.data.mongodb.uri里如果没有用户名密码,连接的就是无认证实例。

Nacos 的情况类似,作为注册中心和配置中心,它的 Namespace 权限模型在旧版本里比较粗糙,默认的nacos/nacos账号如果没改,配置列表和注册实例列表就能被直接读到。这反过来会泄露什么?你所有微服务的配置都可能在 Nacos 里,包括数据库密码、消息队列地址、第三方密钥。

注意:这类问题的判断标准很简单——只要中间件端口能从你的业务网段之外访问到,且没有认证,就应该按未授权访问处理,不要因为"它是内部组件"就降低优先级。

2.5 配置泄露的放大效应:一处的口子可能撬开全局

我特别想强调这一点,因为它经常被低估。单独看/actuator/env泄露或者heapdump下载,好像只是"泄露了一些配置"。但如果你的系统里 JWT 的签名密钥是写在配置文件里的,那么这条链就变成了:/actuator/env泄露jwt.secret,攻击者用这个密钥自己签发一个管理员角色的 Token,业务侧接口的 JWT 校验形同虚设。这时候漏洞的性质就从"信息泄露"升级成了"认证绕过"。

同样的逻辑也适用于 Druid 连接池密码、内部服务间调用的签名盐值、加密用的初始化向量。这些值本身不直接暴露数据,但它们是信任链的根。所以我做安全评审的时候有个习惯:先找哪些配置项是"密钥类"的,再检查这些配置项可能通过哪些路径泄露出去。这个思路比逐个检查端点有效得多,因为它是按"资产价值"而不是按"暴露面数量"来排优先级的。

3. 自查怎么查:源码、运行时、外部视角三层联动

3.1 源码层:先搜配置关键词,五分钟摸清底牌

第一层永远是看代码和配置,因为这是你完全可控的部分。我会在项目根目录跑一组关键词搜索,重点找这几类内容:management.endpoints、exposure、swagger、springdoc、knife4j、druid、h2.console、spring.redis、spring.data.mongodb、eureka.client、nacos。

看的时候关注三件事:开关有没有开、路径有没有改、有没有独立端口。举个具体的判断例子,application.yml里如果出现下面这段,基本可以确认存在问题:

management: endpoints: web: exposure: include: "*" endpoint: health: show-details: always

include: "*"是全量暴露,show-details: always会把健康检查的细节暴露出来,包括数据库连接状态和磁盘路径。这两条单独看都不算致命,组合在一起就是一个完整的信息收集入口。

搜索的时候还要注意多环境配置文件的覆盖关系。application-dev.yml、application-test.yml、application-prod.yml的加载顺序和spring.profiles.active直接相关,经常出现的情况是 prod 配置写得很干净,但application.yml顶层的某个开关没被覆盖掉,结果线上还是开着的。我建议把每个 profile 的生效配置项用/actuator/configprops在测试环境导出一份对照看,虽然这个做法听起来有点绕,但它能发现很多"以为关掉了其实没关"的问题。

3.2 运行时层:从入口逐个验证端点真实状态

源码确认之后,要在运行时验证一遍,因为配置写的和实际生效的可能不一致。做法是从业务入口地址出发,逐个请求管理类路径,看状态码和响应内容。

判断标准我整理成了一张表,方便直接对照:

路径正常状态风险状态风险说明
/actuator404 或 401200 返回端点列表暴露全部可用端点名
/actuator/env404 或 401200 返回属性列表配置项泄露,脱敏不彻底
/actuator/heapdump404 或 401200 返回大体积二进制内存数据泄露,含明文密钥
/actuator/mappings404 或 401200 返回全部路由内部接口清单泄露
/swagger-ui/index.html404 或 401200 返回文档页接口结构完整泄露
/v3/api-docs404 或 401200 返回 JSON接口定义可被程序化解析
/druid/index.html404 或 401200 返回登录页或监控页SQL 与会话监控泄露

验证的时候有个小技巧:不要只看状态码,要看响应体特征。有些项目做了全局异常处理,所有 404 都返回一个 JSON 错误体,这时候状态码是 200 但内容不对。反过来,有些项目把管理端点挡在了统一鉴权后面,返回 401 或 403,这就是正常状态。另外注意 gzip 压缩的情况,heapdump这类端点在压缩后响应体很小,不能靠响应大小来判断,要看Content-Type是不是application/octet-stream这类二进制类型。

3.3 外部视角:把资产测绘当成定期体检

第三层是站在外部视角看。这一步不是为了"攻击自己",而是为了知道"如果我是扫描器,我会看到什么"。做法是把你的域名、IP 段、常见子域名整理成资产清单,用合规的资产梳理工具或者自查脚本从公网侧发起请求,记录哪些管理路径是可访问的。

这里我要提醒一点:做这一步一定要走内部审批,只对自己拥有或明确授权的资产做,不要扫到别人的 IP 段去。技术上,资产测绘的重点是三个维度——域名解析范围、端口开放情况、路径可访问性。很多团队的暴露面问题其实不在应用层,而在于一个测试环境的域名解析到了公网 IP,上面跑着和主站一样的代码,管理端点全开。

我自己的习惯是每个月做一次这样的梳理,把结果和上个月的做对比,新增的可访问路径就是新增的风险。这样做的好处是能发现"配置漂移"——上线时关掉的开关,某次版本发布后又被带回来了。这种问题靠人眼盯配置是盯不住的,只能靠定期扫描出来的结果说话。

4. 加固实操:配置、代码、网关三道闸门

4.1 第一道闸:Actuator 最小化暴露

最有效的加固是最小化。如果你的项目不需要 Actuator 的运维能力,最直接的做法是把它从依赖里去掉。很多项目引入它只是为了用/health做健康检查,而健康检查完全可以用一个自己写的简单接口替代。这一刀砍下去,整个暴露面直接消失。

如果确实需要保留 Actuator,那就从配置上收紧。我推荐的做法有三条:只暴露必要端点、把管理端点放到独立端口、把管理端口限制在内网地址。

management: server: port: 9090 address: 127.0.0.1 endpoints: web: base-path: /_ops exposure: include: health,info,prometheus exclude: env,heapdump,threaddump,mappings,beans,configprops endpoint: health: show-details: never

这几行配置里每一行都有明确的意图。server.port把管理端点从业务端口上剥离出来,address: 127.0.0.1让它只在回环地址上监听,这样即使有人扫到 9090,也只能从本机访问。base-path改成一个非默认值,能让基于默认路径字典的自动化扫描失效,但不要把它当成安全措施,它只是提高门槛。include用白名单列出真正需要的端点,exclude作为双保险再排除一遍敏感项。show-details: never关掉健康检查的细节输出,避免暴露依赖组件的状态。

注意:base-path修改后要同步更新你的监控系统和探针配置,否则健康检查会全部失败导致误报警。这个改动建议先在测试环境跑一轮完整的发布流程再上生产。

4.2 第二道闸:给管理端点加上认证

如果管理端点确实需要跨机器访问,那第二道闸就是认证。Spring Boot 和 Spring Security 的配合很自然,可以用EndpointRequest把管理端点和业务请求分开处理。

@Configuration public class ActuatorSecurityConfig { @Bean public SecurityFilterChain actuatorChain(HttpSecurity http) throws Exception { http.securityMatcher(EndpointRequest.toAnyEndpoint()) .authorizeHttpRequests(auth -> auth .requestMatchers(EndpointRequest.to("health", "info")).permitAll() .anyRequest().hasRole("OPS")) .httpBasic(Customizer.withDefaults()) .csrf(csrf -> csrf.disable()); return http.build(); } }

这段代码的思路是:健康检查这类探针路径允许匿名访问,其余所有管理端点都需要OPS角色。securityMatcher是关键,它让这条过滤链只作用于管理端点,不会影响你的业务接口鉴权逻辑。生产环境里建议把httpBasic换成基于内部证书或统一身份体系的认证方式,基础认证在跨网络传输时还是需要 TLS 保护的。

有一点必须说清楚:这道闸门的前提是业务侧的鉴权链路本身是可靠的。如果你的 JWT 校验存在密钥泄露或者算法混淆之类的问题,那么管理端点的认证也可能被一并绕过。所以加固顺序是先把认证链路的根问题解决掉,再给管理端点加认证,否则就是在沙子上盖楼。

4.3 第三道闸:网关统一收口与路径治理

前两道闸是应用层的,第三道闸放在网关层。不管你的架构是 Nginx 反向代理还是 Spring Cloud Gateway,都可以在入口处对管理类路径做统一拦截。这层的好处是维护成本低,新增服务不用每个都改配置。

Nginx 的写法比较直接:

location ~* ^/(actuator|swagger-ui|v2/api-docs|v3/api-docs|doc.html|druid|h2-console) { deny all; return 403; }

这段配置把常见的敏感路径全部拒绝。要注意~*是大小写不敏感匹配,因为有些框架的路径大小写处理不一致,用不敏感匹配更保险。如果某些路径确实需要对外提供(比如健康检查给负载均衡用),就把它从正则里摘出来单独放行,并且限制来源 IP。

Spring Cloud Gateway 的做法类似,用Path断言配合一个高优先级的过滤器,在请求进入业务路由之前返回 403。这里有个细节值得注意:过滤器的执行顺序要用order明确指定,让它排在鉴权和路由转发之前,否则可能出现请求已经被转发到后端才被拦截的情况,那样既浪费资源也可能在后端日志里留下痕迹。

5. 踩坑实录与常见问题排查

5.1 常见问题速查表

实际排查中遇到的问题大多集中在下面这几类,我整理成了对照表:

现象可能原因排查方向
配置里关了端点但线上仍可访问多 profile 配置未覆盖,或配置中心下发了旧配置对比/actuator/configprops实际生效值,检查配置中心命名空间
返回 401 但仍被判定为高危扫描器把 401 视为"端点存在"结合响应体判断,必要时把路径直接改为 404
修改 base-path 后探针全部失败监控系统未同步更新路径先更新探针配置再发布应用
heapdump响应很小响应被 gzip 压缩检查Content-Type与Content-Encoding头
关掉 Actuator 后 Swagger 还在两套配置相互独立分别处理 Actuator 与文档组件,不要混为一谈
内网环境能访问但公网不能管理端口监听地址受限确认management.server.address配置生效
版本升级后端点路径变了2.x 与 1.x 的默认前缀和暴露策略不同按实际版本核对官方配置项名称

5.2 几个只有踩过才知道的细节

第一个细节是关于回滚的。有些团队在加固时把管理端点的路径和端口全改了一遍,结果监控告警配置没跟着改,发布之后监控瞎了整整一个晚上,第二天才发现。我的建议是加固方案分两次发布:第一次先把暴露范围收窄到白名单,路径和端口不动,观察一周;第二次再改路径和端口,并且提前和监控团队对齐。

第二个细节是关于依赖传递的。你可能没在pom.xml里直接写 Actuator,但某个内部基础组件把它作为依赖带进来了,这种情况很难靠读自己的配置文件发现。排查方法是跑一次mvn dependency:tree或者gradle dependencies,在输出里搜actuator和swagger,看到意外出现的依赖就顺着往上查是谁引入的。

第三个细节是关于版本判断的。很多扫描报告的标题会写"Spring Boot Actuator 未授权访问",但实际的风险等级取决于你暴露了哪些端点。如果只暴露了health,那基本没有实际危害;如果暴露了heapdump或jolokia,那就要按高危处理。收到报告后第一件事是确认实际可访问的端点列表,而不是看到标题就慌。判断顺序是先看有没有二进制下载类端点,再看有没有 MBean 操作类端点,最后看信息类端点,按这个优先级确定修复顺序。

第四个细节是在排查过程中保存证据。做加固之前,把当前的端点响应状态、配置文件内容、依赖树输出都存一份归档,加固之后再采一次,形成前后对比。这有两个好处:一是能量化说明加固效果,二是在出现"改了之后业务异常"的情况时,能快速定位是哪一项配置引起的。我自己维护过一份简单的检查清单,每次发布前过一遍,虽然有点笨,但它确实帮我拦下过好几次配置回潮的问题。

最后分享一个我在实际项目里固定的习惯:把管理类路径的访问全部接入访问日志,并且对来自非内网网段的请求单独打标告警。即便某次配置疏忽让端点短暂暴露了,也能通过日志知道有没有人真的访问过、访问了哪些路径。这比事后去猜"到底有没有被利用"要靠谱得多,也是我觉得投入产出比最高的一项防护措施。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询