When Spring Boot maps a custom health group to an additional server path, actuator path mapping can bypass authentication on subpaths — admin endpoints reachable without credentials.
management.endpoints.web.base-path combined with custom health group server:/healthz mapping — actuator path mapping overrides auth on subpaths like /healthz/admin@PreAuthorize or path-specific security rules as defense-in-depthSpring Boot's actuator module supports custom health groups — named groups of health indicators that can be enabled and configured independently. When a health group is mapped to an additional server path via server:/healthz, the actuator endpoint path mapping delegates to the server's path routing. The problem: the actuator's authentication configuration applies only to the base path, not to subpaths of a mapped server path.
For example, with management.endpoints.web.base-path=/healthz and a custom health group mapped to server:/healthz, the subpath /healthz/admin (admin actuator endpoint) becomes accessible without authentication — the auth requirement from the actuator context does not extend to subpaths that the server routes through the health-group mapped path.
Real-world impact: An attacker who can reach the Spring Boot actuator endpoint (even from a different network segment, as this often bypasses WAF/firewall rules that only check the outer request path) navigates to /healthz/admin and gains access to loggers, beans, environment variables, and the heap dump endpoint. Environment variables contain database passwords, API keys, and cloud credentials. The heap dump contains every object in memory — session tokens, user data, in-flight API calls.
The fix ensures that when Spring Boot registers health group paths with the server, it also registers the corresponding authentication requirements from the actuator security configuration. Subpaths of mapped health group paths now inherit the auth requirement — /healthz/admin requires authentication when management.security.enabled=true.
server:/healthz, the registration happens at the server level, outside the actuator's security filter configuration. The actuator security configuration (management.security.enabled=true) controls access to the actuator context, but when the server handles a request to /healthz/admin, it routes through the health group's path mapping — which was registered without the authentication constraint. The bug is a path registration ordering issue: health group paths are registered before the security filter chain is applied to them. Spring Boot's fix ensures that path registration and security constraint registration happen together as an atomic operation.
management.endpoints.web.base-path=/healthz AND a health group path mapping (server:/healthz) — neither looks dangerous in isolation. The interaction between them is what causes the bypass.management.security.enabled) is in a different properties block or YAML file from the health group path mapping. Reviewers typically look at one or the other, not the security implications of their interaction.
management.endpoint.health.groups=myCustomGroup+management.endpoint.health.group.myCustomGroup.path=/adminmaps the admin health group endpoint to/healthz/admin, but the actuator's security configuration does not apply to subpaths of server-mapped paths. Withmanagement.endpoints.web.base-path=/healthz, the endpoint at/healthz/adminis accessible without authentication — exposing loggers, beans, env, heapdump endpoints. This is CWE-285 (Improper Authorization). Heap dump or env endpoints exposed via this bypass expose database passwords, cloud credentials, and session state. Upgrade to Spring Boot 3.4.15+ / 3.5.12+ / 4.0.4+. As defense-in-depth, add explicit@PreAuthorize("isAuthenticated()")to admin-sensitive actuator endpoints, or apply path-specific security rules in yourSecurityFilterChainbean:request.antMatchers("/healthz/**").authenticated().```suggestion // Fix Option 1: Upgrade Spring Boot to patched version // Fix Option 2: Add explicit Spring Security rules: @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http.authorizeHttpRequests(authz -> authz .requestMatchers("/healthz/**").authenticated() // enforce auth on all actuator subpaths .anyRequest().permitAll() ); return http.build(); } // Fix Option 3: Apply @PreAuthorize to sensitive actuator endpoints @Bean @ConditionalOnProperty(name = "management.endpoint.loggers.enabled", havingValue = "true") public class LoggersEndpointSecurity { @PreAuthorize("isAuthenticated()") public LoggersEndpoint loggersEndpoint() { ... } } ```