Observabilidad de seguridad con Micrometer y trazas

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

La observabilidad es uno de los pilares modernos de las aplicaciones. Sin métricas y trazas, detectar un ataque o un cuello de botella en la capa de seguridad es prácticamente imposible.

Spring Security 6.4+ se integra con Micrometer Observation API, lo que permite exponer cada paso de la cadena de seguridad como una observación que se traduce en métricas Prometheus y trazas OpenTelemetry sin código manual.

Activar observaciones en Spring Security

El soporte está desactivado por defecto. Se habilita con un bean.

@Configuration
public class SecurityObservabilityConfig {

    @Bean
    SecurityObservationSettings observationSettings() {
        return SecurityObservationSettings.withDefaults()
            .shouldObserveRequests(true)
            .shouldObserveAuthentications(true)
            .shouldObserveAuthorizations(true)
            .build();
    }
}

Y necesitamos las dependencias:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
    <groupId>io.micrometer</groupId>
    <artifactId>micrometer-tracing-bridge-otel</artifactId>
</dependency>
<dependency>
    <groupId>io.opentelemetry</groupId>
    <artifactId>opentelemetry-exporter-otlp</artifactId>
</dependency>

Con esto, cada paso del SecurityFilterChain produce una observación con nombre spring.security.filterchains y atributos como secured.uri, error o outcome.

Métricas relevantes para seguridad

Las métricas que conviene graficar en Grafana:

  • spring_security_authentications_seconds_count{outcome="success|failure"}: ratio de logins exitosos vs. fallidos. Un repunte de fallos indica fuerza bruta.
  • spring_security_authorizations_seconds_count{authentication.failure.type=...}: denegaciones por tipo. Pico de AccessDeniedException puede ser enumeración.
  • http_server_requests_seconds_count{status="401|403|429"}: respuestas de error agrupadas. Útil para alertas.
  • spring_security_filterchains_seconds_count{spring.security.reached.filter.section=...}: ayuda a detectar latencia anormal en algún filtro.
management:
  endpoints:
    web:
      exposure:
        include: prometheus, health
  metrics:
    distribution:
      percentiles-histogram:
        spring.security: true
        http.server.requests: true

No incluyas el username como tag de métrica. Genera explosión de cardinalidad. Para auditoría individual, usa logs estructurados o trazas.

Trazas distribuidas con OpenTelemetry

Una traza captura el recorrido completo de una petición a través de varios microservicios. Con Spring Security observable, cada paso de seguridad se convierte en un span dentro de la traza.

management:
  tracing:
    sampling:
      probability: 1.0
  otlp:
    tracing:
      endpoint: "http://otel-collector:4318/v1/traces"

En Tempo, Jaeger o Zipkin verás algo como:

GET /api/pedidos
├── spring.security.filterchains [12 ms]
│   ├── spring.security.authentications [3 ms] outcome=success
│   └── spring.security.authorizations [1 ms] outcome=granted
├── controller.PedidoController#list [8 ms]
└── repository.PedidoRepository#findAll [42 ms]

Las trazas son la herramienta para investigar incidentes: ante un 403 inesperado, abres la traza y ves exactamente qué filter, qué expresión y qué autoridades intervinieron.

Tags personalizados con ObservationConvention

Si necesitas añadir tags propios (por ejemplo, el tenantId o el plan del cliente), implementa un ObservationConvention.

@Component
public class TenantObservationConvention implements ObservationConvention<HttpServerRequestObservationContext> {

    @Override
    public KeyValues getLowCardinalityKeyValues(HttpServerRequestObservationContext context) {
        String tenant = TenantContext.get();
        return KeyValues.of("tenant", tenant != null ? tenant : "unknown");
    }

    @Override
    public boolean supportsContext(Observation.Context context) {
        return context instanceof HttpServerRequestObservationContext;
    }
}

Spring Boot detecta el bean y aplica el convention a todas las peticiones HTTP.

Los low cardinality son tags con pocos valores posibles (tenant, plan, region). Los high cardinality (username, pedidoId) van a metadata de spans, no a métricas.

MDC para correlación en logs

El Mapped Diagnostic Context (MDC) de SLF4J permite que cada línea de log incluya información contextual sin pasarla manualmente.

Spring Security puede poblarlo automáticamente:

@Component
public class SecurityMdcFilter extends OncePerRequestFilter {

    @Override
    protected void doFilterInternal(HttpServletRequest req,
                                    HttpServletResponse res,
                                    FilterChain chain) throws ServletException, IOException {
        Authentication auth = SecurityContextHolder.getContext().getAuthentication();
        if (auth != null && auth.isAuthenticated()) {
            MDC.put("user", auth.getName());
        }
        MDC.put("requestId", req.getHeader("X-Request-Id") != null
            ? req.getHeader("X-Request-Id")
            : UUID.randomUUID().toString());
        try {
            chain.doFilter(req, res);
        } finally {
            MDC.clear();
        }
    }
}

Y configura Logback para incluir esos campos:

<appender name="JSON" class="ch.qos.logback.core.ConsoleAppender">
    <encoder class="net.logstash.logback.encoder.LogstashEncoder">
        <includeMdcKeyName>user</includeMdcKeyName>
        <includeMdcKeyName>requestId</includeMdcKeyName>
        <includeMdcKeyName>traceId</includeMdcKeyName>
    </encoder>
</appender>

Cada log queda enriquecido con user, requestId y traceId. Buscar todos los logs de una petición concreta es tan simple como filtrar por traceId.

SLOs de seguridad

Define SLOs (Service Level Objectives) específicos para la capa de seguridad y aliméntalos con las métricas.

  • SLO de autenticación: 99.9 % de respuestas a /auth/login por debajo de 500 ms.
  • SLO de denegación: ratio 403 / total por debajo del 1 % en horario laboral.
  • SLO de fallos de login: menos de 5 fallos por minuto por IP origen.

Configurarlos en Grafana o Datadog como reglas de alerta:

sum(rate(spring_security_authentications_seconds_count{outcome="failure"}[5m]))
  by (instance)
  > 50

Si la tasa de fallos pasa de 50/s, envía PagerDuty.

Métricas custom para eventos de seguridad

Para auditar eventos específicos (cambios de rol, accesos a datos sensibles, exportaciones), define counters con Micrometer.

@Component
public class AuditMetrics {

    private final Counter rolesCambiados;

    public AuditMetrics(MeterRegistry registry) {
        this.rolesCambiados = Counter.builder("auditoria_roles_cambiados_total")
            .description("Cambios de rol detectados")
            .register(registry);
    }

    public void incrementarCambioRol(String tipo) {
        Counter.builder("auditoria_roles_cambiados_total")
            .tag("tipo", tipo)
            .register(Metrics.globalRegistry)
            .increment();
    }
}

Y en el servicio:

@Transactional
public void asignarRol(Long userId, String nuevoRol) {
    repo.actualizarRol(userId, nuevoRol);
    metrics.incrementarCambioRol(nuevoRol);
}

Detección de anomalías

Las métricas básicas se complementan con detección de anomalías, que compara el patrón actual con el histórico.

  • Prometheus + Grafana: alerta cuando una métrica supera N desviaciones estándar respecto a la media de las últimas 24h.
  • OpenSearch / Elastic SIEM: análisis ML sobre logs.
  • Datadog Watchdog: detección automática de anomalías sin configuración.

Patrones que vale la pena vigilar:

  • Picos de fallos de login concentrados en una IP o un usuario.
  • Accesos exitosos desde geolocalizaciones inusuales (geoip lookup).
  • Cambios de patrón en la distribución de scopes solicitados.
  • Latencia anormal en la verificación de JWT (puede ser ataque al JWK Set).

Errores frecuentes

  • Cardinalidad explosiva: poner userId o requestId como tag rompe Prometheus. Usa labels solo con cardinalidad baja.
  • Loguear Authorization completo: filtra el header Authorization antes de loguear cualquier cosa. Lo mismo con cookies de sesión.
  • No alertar sobre 401/403: muchas organizaciones solo monitorizan 5xx. Los 4xx son la pista número uno de un ataque en curso.
  • Confiar en logs sin trazas: un 403 sin contexto cuesta horas debugar. Las trazas dan el "cómo se llegó hasta aquí" gratis.
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

Instrumentar la cadena de filtros de Spring Security con Micrometer Observation, exponer métricas y trazas con tags útiles y enriquecer logs con MDC sin filtrar PII.