UnveilTech

UnveilScan Blog

← All articles

Try UnveilScan free

The forgotten /actuator endpoint: Spring Boot info disclosure in 2026

Posted 2026-04-29 · 6 min read · WEBSpring

Spring Boot ships with the spring-boot-starter-actuator dependency in just about every project template, scaffold, and tutorial since 2017. Its purpose is internal operations: health checks, metrics, request traces, JVM introspection, runtime config inspection. None of which should be reachable from the public internet. All of which frequently are.

The default config trap

Pre-Spring Boot 2.0, all actuator endpoints were exposed by default. Devs complained. From 2.0 onwards, only /actuator/health and /actuator/info are exposed by default. So, "fixed", right?

Then someone, somewhere, in pursuit of a metrics dashboard, adds:

# application.properties
management.endpoints.web.exposure.include=*

And now /actuator/env, /actuator/heapdump, /actuator/threaddump, /actuator/loggers, /actuator/configprops, /actuator/httptrace... are all reachable. From the open internet. With no authentication.

We've found this in production at companies you've heard of. Two CRITICAL hits in our scan database came from /actuator/heapdump directly serving JVM heap dumps to anonymous requests. A heap dump contains every secret, every session, every credential the JVM knew at dump time.

The bad endpoints, ranked

EndpointWhat it leaksUnveilScan severity
/actuator/heapdumpFull JVM heap (binary, ~100 MB)CRITICAL
/actuator/envEvery env var + Spring property + DB URLCRITICAL
/actuator/threaddumpThread states, stack traces with method argsHIGH
/actuator/configpropsBound configuration class fieldsHIGH
/actuator/loggersLogger names + levels (POST can change them — log injection)HIGH
/actuator/httptraceLast 100 HTTP requests with full headers (cookies, auth)HIGH
/actuator/mappingsEvery controller endpoint + parametersMEDIUM
/actuator/healthService status — varies by configLOW (often expected)

How attackers use this

  1. Find a Spring Boot app via the X-Application-Context header or paths like /actuator/health returning {"status":"UP"}.
  2. Probe /actuator/env. If 200 OK with JSON body, gold mine: read DB URLs, S3 keys, JWT secrets, API tokens.
  3. Probe /actuator/heapdump. If 200 with binary body, download. Open in Eclipse MAT or VisualVM. Search for "password", "secret", "token". Find session tokens still active.
  4. Probe /actuator/loggers. POST a config change to log the next 100 requests at TRACE level. Now you log all your own traffic to your own attacker-controlled appender.

Fix in 3 lines

# application.properties
# Only the bare minimum — health for load balancers, info for ops dashboards.
management.endpoints.web.exposure.include=info,health
# Bind actuator to a different port (loopback only, behind firewall)
management.server.port=8081
management.server.address=127.0.0.1

Or in application.yml:

management:
  endpoints:
    web:
      exposure:
        include: info,health
  server:
    port: 8081
    address: 127.0.0.1

The actuator listener now binds 127.0.0.1:8081. External traffic on 80/443 can't reach it. Internal monitoring (Prometheus scraper on same host) still can.

Authenticating actuator

If you genuinely need actuator from outside the box (a remote monitoring service), add Spring Security:

@EnableWebSecurity
public class ActuatorSecurity extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
            .requestMatchers(EndpointRequest.to(HealthEndpoint.class, InfoEndpoint.class)).permitAll()
            .requestMatchers(EndpointRequest.toAnyEndpoint()).hasRole("ACTUATOR")
            .and().httpBasic();
    }
}

Plus spring.security.user.name=... + a strong password — or, better, OAuth.

How UnveilScan checks this

Our api_surface checker probes 14 paths including /actuator, /actuator/env, /actuator/heapdump, plus equivalents for other frameworks (graphql, swagger, openapi). Severity scaled by what we find:

A canary __unveilscan_canary_nx probe rules out catch-all 200s before flagging anything. False-positive rate is ~0%.

Probe your /actuator surface

Free Basic scan reports the obvious endpoints. Extended adds 5 more probes.

Scan a domain