Securizar Spring Boot Actuator: endpoints y métricas

Avanzado
Spring Security
Spring Security
Actualizado: 21/04/2026

Spring Boot Actuator expone información de monitorización imprescindible: salud, métricas, configuración, hilos, memoria. Esa misma información, en manos equivocadas, es un mapa detallado para atacar la aplicación.

Por defecto, solo /actuator/health y /actuator/info son accesibles vía HTTP. El resto requiere autenticación, pero la configuración por defecto no es suficiente para producción. Esta lección cubre cómo securizar Actuator de forma sólida.

Endpoints sensibles que conviene revisar

| Endpoint | Riesgo si es público | |---|---| | /actuator/env | Revela todas las propiedades, incluida URL de BD y secrets | | /actuator/configprops | Lista de configuration properties con valores | | /actuator/heapdump | Volcado completo del heap, contiene strings con tokens | | /actuator/threaddump | Stack traces que pueden filtrar lógica interna | | /actuator/httpexchanges | Últimas peticiones con cabeceras (incluida Authorization) | | /actuator/loggers | Permite cambiar el nivel de logs en caliente, útil para inundar disco | | /actuator/shutdown | Para la aplicación | | /actuator/beans | Lista beans, util para mapear arquitectura interna |

Heap dump y env son los dos vectores más explotados en CVEs de Actuator. Trátalos como contraseñas: solo accesibles desde la red interna y con autenticación.

Configuración base segura

Empezamos por exponer solo los endpoints necesarios y nunca los administrativos.

management:
  endpoints:
    web:
      exposure:
        include: health, info, prometheus, metrics
  endpoint:
    health:
      probes:
        enabled: true
      show-details: when-authorized
      show-components: when-authorized
  info:
    git:
      mode: full
  • exposure.include en blanco lista lo que se expone, no lo que se oculta. Es la opción segura.
  • show-details: when-authorized evita revelar el estado de cada componente (BD, Redis, Kafka) a anónimos.

Para producción, excluye explícitamente lo peligroso aunque pienses que no está:

management:
  endpoints:
    web:
      exposure:
        include: "*"
        exclude: env, configprops, heapdump, threaddump, beans, mappings, loggers, shutdown

Separar el management port

Una buena práctica en empresa es exponer Actuator en un puerto distinto al de la API pública. Ese puerto solo escucha desde la red de monitorización (Prometheus, Grafana, operadores).

server:
  port: 8080
management:
  server:
    port: 9090
    address: 127.0.0.1

Con esto, http://app:9090/actuator/* solo es accesible desde localhost. En Kubernetes, el Service que expone el management port nunca tiene LoadBalancer público; está restringido al namespace de monitoring por NetworkPolicy.

El truco es defensa en profundidad: aunque un Pod malicioso entrara al cluster, no podría llegar al management port si las NetworkPolicy están bien escritas.

SecurityFilterChain dedicado

Spring Security puede aplicar reglas distintas al management port mediante un SecurityFilterChain con securityMatcher específico.

@Configuration
public class ActuatorSecurityConfig {

    @Bean
    @Order(1)
    SecurityFilterChain actuator(HttpSecurity http) throws Exception {
        http
            .securityMatcher(EndpointRequest.toAnyEndpoint())
            .authorizeHttpRequests(auth -> auth
                .requestMatchers(EndpointRequest.to(HealthEndpoint.class, InfoEndpoint.class)).permitAll()
                .requestMatchers(EndpointRequest.to("prometheus")).hasAuthority("ROLE_PROMETHEUS")
                .anyRequest().hasAuthority("ROLE_ACTUATOR"))
            .httpBasic(Customizer.withDefaults())
            .csrf(csrf -> csrf.disable());
        return http.build();
    }

    @Bean
    @Order(2)
    SecurityFilterChain api(HttpSecurity http) throws Exception {
        http
            .authorizeHttpRequests(auth -> auth.anyRequest().authenticated())
            .oauth2ResourceServer(oauth -> oauth.jwt(Customizer.withDefaults()));
        return http.build();
    }
}

EndpointRequest.toAnyEndpoint() matchea solo URLs de Actuator. EndpointRequest.to(...) permite filtrar por nombre o por clase.

El orden importa. La cadena más específica va primero (@Order(1)). Si la API se evalúa antes, los endpoints de Actuator quedarán bajo la política general y romperá la separación.

Usuarios para Prometheus, operador y CI

Define usuarios diferenciados con autoridades específicas. Cada cliente accede solo a lo que necesita.

@Bean
UserDetailsService actuatorUsers(PasswordEncoder encoder) {
    UserDetails prom = User.withUsername("prometheus")
        .password(encoder.encode(System.getenv("PROMETHEUS_TOKEN")))
        .authorities("ROLE_PROMETHEUS")
        .build();

    UserDetails ops = User.withUsername("ops")
        .password(encoder.encode(System.getenv("OPS_TOKEN")))
        .authorities("ROLE_ACTUATOR")
        .build();

    return new InMemoryUserDetailsManager(prom, ops);
}
  • prometheus: scrappeo automatizado, solo /actuator/prometheus.
  • ops: operador humano, acceso completo (no a /heapdump y /env, según política).

Las credenciales NO van en application.yaml. Llegan como variables de entorno desde el secret manager.

Health endpoint con detalles selectivos

El endpoint /actuator/health es público por necesidad: lo consultan los probes de Kubernetes y los balanceadores. Pero su salida puede revelar más de lo conveniente.

management:
  endpoint:
    health:
      show-details: never
      show-components: never
      group:
        readiness:
          include: db, redis
          show-details: never
        liveness:
          include: ping

Con show-details: never, la respuesta es simplemente:

{ "status": "UP" }

Suficiente para los probes. Los detalles solo se muestran al operador que llega autenticado al endpoint general (no a los grupos readiness y liveness).

Métricas con Micrometer y dimensiones de seguridad

Micrometer es la abstracción de métricas de Spring Boot. Para enriquecer las métricas con información de seguridad sin filtrar PII, conviene definir tags acotados.

@Component
public class SecurityMetricsConfig implements MeterRegistryCustomizer<MeterRegistry> {

    @Override
    public void customize(MeterRegistry registry) {
        registry.config()
            .commonTags("application", "pagos-service", "tenant", System.getenv("TENANT"))
            .meterFilter(MeterFilter.deny(id ->
                id.getName().startsWith("jvm.classes.loaded")));
    }
}

Los counters habituales de seguridad son:

  • spring_security_authentications_total{outcome="success|failure"}
  • spring_security_filterchains_active{name="..."}
  • http_server_requests_seconds{status="401|403|429"}

No incluyas username como tag de métrica. Genera alta cardinalidad (un timeseries por usuario) que hunde Prometheus. Para cardinality alta, usa logs estructurados o trazas.

Auditoría de cambios en Actuator

Si permites cambiar loggers o disparar shutdown, registra todas esas operaciones en logs de auditoría. Spring Security publica AuthenticationSuccessEvent y AuthorizationDeniedEvent; suscríbete y persístelos.

@Component
public class ActuatorAuditListener {

    private static final Logger log = LoggerFactory.getLogger("AUDIT");

    @EventListener
    public void onSuccess(AuthenticationSuccessEvent event) {
        Authentication auth = event.getAuthentication();
        if (auth.getDetails() instanceof WebAuthenticationDetails web
            && web.getRemoteAddress() != null) {
            log.info("AUTH OK user={} ip={}", auth.getName(), web.getRemoteAddress());
        }
    }
}

Y registra qué endpoint se ha invocado con un Filter que loguea método, path y usuario.

CSRF en Actuator

Endpoints como /actuator/loggers/{name} aceptan POST. CSRF está activo por defecto, lo que rompe los scripts de operación.

  • Si Actuator usa Basic Auth con cliente automatizado, deshabilita CSRF en su SecurityFilterChain. Un cliente que envía Authorization Basic no es vulnerable a CSRF (la cookie no se reusa).
  • Si Actuator se usa solo desde navegador con sesión, mantén CSRF activo y exige el token.
.csrf(csrf -> csrf.disable())

Disable CSRF solo cuando el contexto sea exclusivamente Basic Auth o JWT. Nunca cuando haya cookies de sesión.

Errores frecuentes

  • Exponer *: incluye /heapdump y /env por defecto. Usa lista blanca explícita.
  • Reusar el SecurityFilterChain de la API: las reglas de la API REST (csrf.disable, STATELESS) suelen no encajar con Actuator. Sepáralos.
  • Olvidar management.server.address: si lo dejas en 0.0.0.0, Actuator queda accesible desde fuera.
  • Métricas con username: alta cardinalidad y, en algunos sectores, viola GDPR al exportar datos personales a Prometheus.
Alan Sastre - Autor del tutorial

Alan Sastre

Ingeniero de Software y formador, CEO en CertiDevs

Ingeniero de software especializado en Full Stack y en Inteligencia Artificial. Como CEO de CertiDevs, Spring Security es una de sus áreas de expertise. Con más de 15 años programando, 6K seguidores en LinkedIn y experiencia como formador, Alan se dedica a crear contenido educativo de calidad para desarrolladores de todos los niveles.

Más tutoriales de Spring Security

Explora más contenido relacionado con Spring Security y continúa aprendiendo con nuestros tutoriales gratuitos.

Aprendizajes de esta lección

Configurar Spring Boot Actuator para exponer solo endpoints seguros, separar el management port, restringir acceso con SecurityFilterChain dedicado y controlar quién consulta heap dumps o thread dumps.