Spring Boot Actuator en producción: los endpoints que dejé abiertos sin darme cuenta y cómo los cerré
Estaba revisando la configuración de un backend Spring Boot 3.x que vengo construyendo — el mismo que discutí en el post de .
Capa 2 — Spring Security
La configuración de properties es necesaria pero no suficiente. Si Security está mal configurado, o si alguien toca esa config en el futuro sin contexto, todo puede reabrirse. La segunda capa es el cinturón de seguridad:
@Configuration
@EnableWebSecurity
public class ActuatorSecurityConfig {
@Bean
public SecurityFilterChain actuatorFilterChain(HttpSecurity http) throws Exception {
http
// Aplicamos esta config solo a rutas de Actuator
.securityMatcher("/actuator/**")
.authorizeHttpRequests(auth -> auth
// Health e info son públicos — para health checks del load balancer
.requestMatchers("/actuator/health/**").permitAll()
.requestMatchers("/actuator/info").permitAll()
// Métricas solo para usuarios con rol MONITORING
.requestMatchers("/actuator/metrics/**").hasRole("MONITORING")
// Cualquier otro endpoint Actuator requiere ADMIN
.anyRequest().hasRole("ADMIN")
)
// Actuator no necesita CSRF — es API interna
.csrf(csrf -> csrf
.ignoringRequestMatchers("/actuator/**")
)
// Sin sesiones para Actuator — stateless
.sessionManagement(session -> session
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
)
.httpBasic(Customizer.withDefaults());
return http.build();
}
}
Este enfoque usa el patrón de múltiples SecurityFilterChain que Spring Boot 3.x recomienda. La chain de Actuator tiene su propia lógica y no interfiere con la seguridad del resto de la app.
Los gotchas que nadie menciona en los tutoriales
Después de cerrar todo y correr la auditoría de nuevo, encontré tres situaciones que me atraparon y que vale la pena documentar.
1. /actuator/health detallado rompe health checks del load balancer
Cuando configurás show-details=when-authorized, el endpoint /actuator/health devuelve 200 OK con body mínimo para requests no autenticados — lo cual es correcto. Pero algunos health checkers corporativos esperan ver "status": "UP" en el body y parsean el JSON. Verificá que el health checker que usás funcione con el body reducido:
{ "status": "UP" }
Railway usa el status HTTP (200 = healthy), no el body — así que ahí no hay drama. Pero si venís de un setup con AWS ALB o un probe de Kubernetes que valida el body, probalo antes de deployar.
2. management.endpoint.X.enabled=false vs exposure.exclude — no son lo mismo
exposure.excludesaca el endpoint de la lista HTTP pero lo deja habilitado internamente (JMX, etc.)
enabled=falselo deshabilita completamente en todos los transports
Para endpoints como heapdump y env, usá ambos. La razón: si alguien en el futuro agrega una dependencia de monitoring que habilita JMX, un endpoint solo excluido de HTTP puede reaparecer.
3. El /actuator/loggers POST es una puerta de escritura
/actuator/loggers/{name} acepta POST para cambiar niveles de log en runtime. Si ese endpoint queda abierto, cualquier atacante puede subir el nivel de logging a TRACE y potencialmente generar logs enormes (disk exhaustion) o bajar niveles de seguridad a OFF. Cerrarlo no es opcional.
Comparativa del surface de ataque antes y después
Corrí el mismo script de auditoría antes y después del hardening. El resultado:
# Antes del hardening (con exposure.include=*)
Endpoints accesibles sin auth: 10
Endpoints con información sensible: 3 (env, beans, heapdump)
Endpoints con capacidad de escritura: 2 (loggers, shutdown*)
# Después del hardening
Endpoints accesibles sin auth: 2 (health básico, info)
Endpoints con información sensible accesibles sin auth: 0
Endpoints con capacidad de escritura accesibles sin auth: 0
El shutdown endpoint merece mención especial: está deshabilitado por defecto en Spring Boot 3.x, pero si alguna vez lo habilitaste para testing y olvidaste revertirlo, es un POST /actuator/shutdown que mata la JVM. Lo verifico explícitamente en el script de auditoría.
Este tipo de superficie de ataque es relevante si estás pensando en seguridad end-to-end, incluyendo el cifrado de datos en tránsito — algo que exploré en más profundidad en el post de es clara en decir que "para producción, te recomendamos asegurarte de que solo los endpoints de health e info estén expuestos". Pero el tono es de recomendación, no de advertencia fuerte. Y los tutoriales virales de "integrar Prometheus con Spring Boot" pasan directo a exposure.include=* sin mencionar que eso abre el heapdump al mundo.
Mi punto después de armar este checklist: Actuator es una herramienta poderosa de observabilidad, pero viene configurada para conveniencia de desarrollo, no para resiliencia de producción. El costo de no auditarlo es alto; el costo de cerrarlo correctamente es bajo — una tarde de trabajo, dos archivos de configuración.
Si venés de leer el post de , ya sabés que mi approach es validar en escenarios concretos antes de dar recomendaciones. Acá aplica lo mismo: no confíes en el default. Corrés el script de auditoría, ves qué devuelve, y después decidís qué cerrar.
El checklist final que uso ahora para cualquier backend Spring Boot antes de ir a producción:
# Checklist Actuator pre-producción — Spring Boot 3.x
# 1. Verificar qué endpoints están expuestos
curl -s http://localhost:8080/actuator | python3 -m json.tool
# 2. Confirmar que /actuator/env devuelve 401 o 404
curl -s -o /dev/null -w "%{http_code}" http://localhost:8080/actuator/env
# 3. Confirmar que /actuator/heapdump devuelve 401 o 404
curl -s -o /dev/null -w "%{http_code}" http://localhost:8080/actuator/heapdump
# 4. Confirmar que /actuator/beans devuelve 401 o 404
curl -s -o /dev/null -w "%{http_code}" http://localhost:8080/actuator/beans
# 5. Verificar que health básico sigue respondiendo para el load balancer
curl -s http://localhost:8080/actuator/health
# Resultado esperado en producción:
# env → 401 o 404 ✓
# heapdump → 401 o 404 ✓
# beans → 401 o 404 ✓
# health → 200 con {"status":"UP"} ✓
Si alguno de los primeros tres devuelve 200 sin autenticación, parás el deploy y lo corregís. No hay excusa.
Fuentes originales:
- Spring Boot Actuator documentation: ↗ Original-Artikel auf dev.to lesenVollständiger Original-BerichtAusführliche Details, Code-Beispiele & Hersteller-Stellungnahme auf dev.to.
SOCIAL SHARE CARD GENERATOR