← News
/PLAYBOOK · 2026-08-19

Spring Boot Actuator Unauthorized Access: Production Endpoint Hardening

Unprotected Spring Boot Actuator endpoints leak memory, config, and secrets, and can invoke shutdown. This guide covers three production hardening methods: Maven dependency control, disabling sensitive endpoints, and Spring Security auth, with full code and verification steps.

Scope

Unauthenticated Spring Boot Actuator is a high-frequency production risk. Developers enable actuator and skip auth. Attackers hit /actuator/env for database passwords, /actuator/heapdump for heap dumps and extracted keys, or /actuator/shutdown to kill the app. Applies to: Spring Boot 2.x/3.x pre-production security review, emergency containment of exposed actuator, and teams that must meet MLPS controls for sensitive-data leakage.

Prerequisites

Mechanism

Actuator is Spring Boot's production monitoring component. Default exposure is health, but developers often set management.endpoints.web.exposure.include=* and open everything. Sensitive endpoints:

Defense: minimum exposure + mandatory auth. Production exposes only health/info. Everything else off. Sensitive endpoints that must stay on are gated by Spring Security (IP allowlist or account auth).

Procedure

Step 1: Trim exposed endpoints (first)

In application.yml, expose only health checks. Disable heapdump/shutdown/env and other sensitive endpoints (key config):

`# application.yml
management:
  endpoints:
    web:
      exposure:
        include: health,info
  endpoint:
    health:
      show-details: never        # do not expose database or other details
    shutdown:
      enabled: false             # explicitly disable shutdown`

Change the default path to cut scanner hits:

`management:
  endpoints:
    web:
      base-path: /internal-monitor  # default is /actuator; change to a random path`

Step 2: Enforce auth with Spring Security

If some sensitive endpoints must remain, intercept with Security. Add to pom.xml:

`<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>`

Config class (Spring Boot 3.x):

`@Configuration
@EnableWebSecurity
public class ActuatorSecurityConfig {
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
          .securityMatcher("/internal-monitor/**")
          .authorizeHttpRequests(auth -> auth.anyRequest().hasRole("MONITOR"))
          .httpBasic(Customizer.withDefaults());
        return http.build();
    }
}`

Define the monitor account in application.yml (inject via env var in production; do not hardcode):

`spring:
  security:
    user:
      name: monitor
      password: ${MONITOR_PASSWORD}   # inject a strong password via environment variable
      roles: MONITOR`

Step 3: Port isolation (advanced)

Bind actuator to an internal management port, physically separate from the app port:

`management:
  server:
    port: 9081                    # dedicated port, not exposed externally
    address: 127.0.0.1            # localhost only; pair with Nginx internal reverse proxy`

Step 4: Live self-check commands

`# check whether sensitive endpoints are anonymously accessible (should return 401 or 404)
curl -i http://your-app:8080/actuator/env | head -5
curl -i http://your-app:8080/actuator/heapdump -o /dev/null -w "%{http_code}\\n"
# if already hardened, should return 401 (auth required) or 404 (not exposed) respectively`

Verification

`# 1) rebuild the package and start
mvn clean package -DskipTests && java -jar target/app.jar
# 2) unauthenticated access to sensitive endpoints -> 401
curl -i http://localhost:8080/actuator/env | grep -E "HTTP/|401"
# 3) access with the monitor account -> 200 and returns health data only
curl -u monitor:password http://localhost:8080/actuator/health
# 4) confirm the heapdump endpoint no longer exists -> 404
curl -i http://localhost:8080/actuator/heapdump | head -3
# 5) if a dedicated port is configured, /actuator on business port 8080 should return 404 directly`

FAQ

FAQ 1: include: health,info is set but /actuator/env is still reachable?

Check overlapping config files (application-dev.yml overriding include) or leftover management.endpoints.web.exposure.include=*. Unify with spring.config.import and confirm the effective include value in startup logs.

FAQ 2: After Spring Security, business APIs also require login?

securityMatcher did not take effect. Security intercepts all requests by default. Constrain with securityMatcher(/internal-monitor/**) on Spring Boot 3.x. Spring Boot 2.x uses antMatchers. The APIs are not interchangeable.

FAQ 3: health must stay for cloud probe/liveness?

Keep health exposed. Set show-details: never (Spring Boot 2.x never; 3.x never/always) so the probe does not leak database state or version. Restrict probe sources with an IP allowlist.

Summary

Unauthenticated Actuator defense is minimum exposure + mandatory auth + port isolation. Production exposes only health/info; sensitive endpoints off. Endpoints that must remain get Spring Security account/IP dual restriction. Advanced: isolate actuator on an internal port. Put this on the go-live security checklist. After deploy, curl every sensitive endpoint immediately.