☁️Entra IDApplicationsMonitoringRisk ProtectionConditional Access

Entra Service Principal: Detección de Anomalías de Inicio de Sesión, Puntos Ciegos de la Identidad de Carga de Trabajo

Los service principals no pueden hacer MFA, el Acceso Condicional habitual no los cubre, y sus inicios de sesión viven en un flujo de logs separado. Aprenda a establecer una línea base y detectar anomalías en las identidades que poseen sus permisos de Graph más amplios.

Younes AZABARPor Younes AZABAR18 min de lectura
Entra Service Principal: Detección de Anomalías de Inicio de Sesión, Puntos Ciegos de la Identidad de Carga de Trabajo

Entra Service Principal: Detección de Anomalías de Inicio de Sesión, Identidad de Carga de Trabajo Definida

Entra Service Principal: la detección de anomalías de inicio de sesión, la supervisión de identidad de carga de trabajo que establece una línea base de cómo se autentican las cuentas de máquina, es el control que la mayoría de los tenants nunca configura. Todos los demás controles de identidad de un tenant maduro — campañas de inscripción de MFA, métodos de autenticación resistentes al phishing, Acceso Condicional basado en riesgo — están construidos alrededor de un humano presente frente al teclado. Los service principals no tienen humano, y esos controles simplemente no los alcanzan.

Microsoft es explícito sobre por qué estas identidades son diferentes. Una identidad de carga de trabajo es una identidad que permite a una aplicación o service principal acceder a recursos, y la propia documentación de Microsoft enumera tres propiedades que la distinguen de una cuenta de usuario: las identidades de carga de trabajo "No pueden realizar autenticación multifactor", "A menudo no tienen un proceso de ciclo de vida formal", y "Necesitan almacenar sus credenciales o secretos en algún lugar".

Leyendo estos tres puntos juntos, la exposición se vuelve evidente. La credencial es una cadena de texto guardada en una variable de pipeline, un Key Vault, un archivo appsettings, o una capa de imagen de contenedor. No hay un segundo factor detrás, ningún ciclo de vida que la expire el último día de una baja, y — porque un inicio de sesión de service principal no implica ningún usuario — nada que parezca inusual para una pila de alertas centrada en el humano. Una vez que esa cadena se filtra, el atacante se autentica exactamente como lo hace la aplicación, y el inicio de sesión tiene éxito en el primer intento.

⚠️

⚠️ Advertencia: Después de que un secreto de service principal se filtra, no hay contraseña que rociar, ninguna solicitud MFA que fatigar, y ningún usuario al que hacer phishing. La única señal que queda es la forma de los inicios de sesión: cuántos, desde dónde, en qué red, con qué credencial.

Por Qué Su Cobertura de MFA y Acceso Condicional No Aplica

El Acceso Condicional es el control que los administradores asumen que lo cubre todo. No es así — las identidades de carga de trabajo son uno de varios puntos donde su cobertura se detiene silenciosamente, junto a los errores de alcance y exclusión catalogados en Brechas de Acceso Condicional en Azure: Qué Errores Dejan una Exposición Real. La documentación de Microsoft establece que las políticas de Acceso Condicional "históricamente solo se aplicaban a los usuarios cuando accedían a aplicaciones y servicios", y que el soporte para service principals es una capacidad separada llamada Acceso Condicional para identidades de carga de trabajo.

Esa capacidad separada conlleva restricciones que determinan si su política realmente hace algo:

RestricciónLo que indica la documentación de Microsoft
Licencia"Se requieren licencias Workload Identities Premium para crear o modificar políticas de Acceso Condicional dirigidas a service principals." Las políticas existentes siguen funcionando en directorios sin licencia pero no pueden modificarse.
Alcance"La política puede aplicarse a service principals de un solo tenant registrados en su tenant. Las aplicaciones SaaS de Microsoft y de terceros, incluidas las aplicaciones multi-tenant, no están cubiertas por estas políticas. Las identidades administradas no están cubiertas por la política."
Asignación por grupo"Aunque los service principals pueden añadirse a grupos, las políticas de Acceso Condicional asignadas a un grupo que contiene un service principal no se aplican a ese service principal." Debe asignarse directamente como identidad de carga de trabajo.
Controles disponiblesBajo Conceder, "Bloquear el acceso es la única opción disponible." No hay un "requerir MFA" al que recurrir.

La regla de asignación por grupo merece énfasis, porque falla en silencio. Un administrador que coloca service principals en un grupo de seguridad ya objetivo de una política de Acceso Condicional verá una política que parece correctamente delimitada en el portal y que no aplica absolutamente nada a esos service principals. Nada da error. Nada advierte. La política simplemente no se evalúa para ellos.

Las condiciones soportadas son deliberadamente estrechas: bloquear service principals "fuera de rangos de IP públicas conocidos", bloquear "según el riesgo detectado por Microsoft Entra ID Protection", y contextos de autenticación. Ubicación y riesgo — no el dispositivo, no el MFA, no el cumplimiento.

También existe una asimetría entre detectar el riesgo y bloquearlo, fácil de confundir. ID Protection "detecta riesgo en aplicaciones de un solo tenant, SaaS no-Microsoft, y multi-tenant", mientras que la aplicación del Acceso Condicional solo alcanza a los service principals de un solo tenant. Las identidades administradas quedan fuera de ambos. Así que para una aplicación SaaS multi-tenant en su tenant, se le puede informar que está comprometida sin poder bloquearla con una política de identidad de carga de trabajo — lo que convierte a la detección y la respuesta, no a la prevención, en el control operante.

Dónde Viven Realmente los Inicios de Sesión de los Service Principals

La segunda razón por la que estos inicios de sesión no se vigilan es que la mayoría de las consultas de hunting nunca los tocan. Microsoft Entra registra cuatro tipos de logs de inicio de sesión: inicios de sesión de usuario interactivos, inicios de sesión de usuario no interactivos, inicios de sesión de service principal, e inicios de sesión de identidad administrada. Un SOC que construyó sus detecciones sobre los inicios de sesión de usuario no está mirando un subconjunto filtrado de una tabla — está mirando un flujo completamente distinto.

El flujo de los service principals es autónomo. Microsoft lo describe claramente: "A diferencia de los inicios de sesión de usuario interactivos y no interactivos, los inicios de sesión de service principal no involucran a un usuario. En su lugar, son inicios de sesión de cualquier cuenta no-usuario, como aplicaciones o service principals... En estos inicios de sesión, la aplicación o el servicio proporciona su propia credencial, como un certificado o un secreto de aplicación, para autenticarse o acceder a recursos."

Cuando se exportan a un espacio de trabajo de Log Analytics mediante Configuración de diagnóstico, aterrizan en la tabla AADServicePrincipalSignInLogs — no SigninLogs. Si nunca ha habilitado esa categoría de log, las consultas de la siguiente sección devuelven cero filas, y cero filas se verán exactamente igual que un entorno limpio.

La retención es la otra mitad del prerequisito. La referencia de retención de datos de Microsoft otorga a los logs de inicio de sesión siete días en Microsoft Entra ID Free y 30 días en P1 o P2. Una detección de comportamiento necesita una línea base más una ventana reciente dentro de ese presupuesto — no se puede construir un perfil de 90 días a partir de un log de 30 días. La configuración de la exportación se cubre en Registro de Entra ID: Retención, Configuración de Diagnóstico, Lagunas de Exportación SIEM; trátelo como un prerequisito estricto para todo lo que sigue.

Estas son las columnas que llevan la señal:

ColumnaPor qué importa
AppId, ServicePrincipalNameLa identidad a la que establecer línea base. AppId es la clave de agrupación estable.
IPAddress, LocationDetails, AutonomousSystemNumberDesde dónde se autenticó la carga de trabajo. La red de una carga de trabajo suele ser mucho más estable que la de un humano.
ServicePrincipalCredentialKeyId, ClientCredentialTypeQué credencial se usó, y si fue un secreto de cliente o una aserción de cliente.
ResourceDisplayName, ResourceIdentityA qué accedió. Nuevos recursos objetivo son una señal de pivote fuerte.
ResultType"0" para éxito. Los fallos son una investigación distinta.
ConditionalAccessStatusSi siquiera se evaluó una política de identidad de carga de trabajo.
FederatedCredentialIdSe completa cuando se usó una credencial de identidad federada en lugar de un secreto almacenado.

La Cadena de Ataque Después de que un Secreto se Filtra

Paso 1 — La credencial se escapa

El secreto sale del entorno: subido a un repositorio público, capturado desde un log de build, extraído de una estación de trabajo de desarrollador comprometida, o comprado. La detección Leaked Credentials de Microsoft para identidades de carga de trabajo existe precisamente porque esto es rutinario — verifica credenciales obtenidas "de GitHub, la dark web, sitios de paste, u otras fuentes" contra las credenciales válidas del tenant. El mismo servicio cubre las cuentas de usuario, donde el fallo recurrente está en la respuesta más que en la detección — vea Entra ID Risk Protection: Credenciales Filtradas, Usuarios de Riesgo No Remediados.

Paso 2 — Una autenticación que parece completamente legítima

El atacante realiza un flujo OAuth client credentials con el secreto robado. Ningún MFA es posible, así que no falta ninguno. Si no hay ninguna política de identidad de carga de trabajo asignada directamente, ConditionalAccessStatus refleja un inicio de sesión que nada evaluó. El resultado es un inicio de sesión exitoso que es, byte por byte, la misma operación que la aplicación real realiza mil veces al día. Solo el contexto circundante difiere.

Paso 3 — Persistencia añadiendo una credencial

En lugar de depender de un secreto que puede rotarse, un atacante con privilegios suficientes añade su propia credencial al objeto de la aplicación o del service principal. La identidad ahora tiene dos credenciales válidas, y revocar la que se filtró no cambia nada. Este paso deja dos rastros: un cambio de directorio en el log de auditoría, y — en cada inicio de sesión posterior — un ServicePrincipalCredentialKeyId que nunca antes había aparecido para ese AppId.

Paso 4 — Actuar a través de Graph

Los permisos de aplicación del service principal son ahora el radio de impacto, y los permisos de aplicación no están limitados por los derechos de ningún usuario. Concesiones a nivel de tenant como Directory.ReadWrite.All o RoleManagement.ReadWrite.Directory convierten un secreto filtrado en control sobre todo el directorio; vea Entra App Registration: Permisos Graph API Peligrosos sobre cómo se inventarían y reducen estas concesiones. La detección Suspicious API Traffic de Microsoft apunta a esta etapa, disparándose "cuando se observa tráfico anómalo de GraphAPI o enumeración del directorio por parte de un service principal".

Detección

La detección de comportamiento aquí se basa en una propiedad que juega a favor del defensor: las cargas de trabajo son aburridamente consistentes. Un trabajo de sincronización nocturno se ejecuta a la misma hora, desde la misma IP de salida, contra el mismo recurso, con la misma credencial. Los humanos son erráticos; la automatización no lo es. Esa consistencia hace que la desviación sea significativa — y es por eso que las detecciones equivalentes para cuentas humanas, como el viaje imposible y el replay de token AiTM, tienen que tolerar mucho más ruido que las consultas de abajo.

IndicadorDónde buscarCampos claveQué significa
Pico de volumen de inicios de sesiónAADServicePrincipalSignInLogsAppId, conteo en el tiempoRecolección masiva o enumeración mediante un token robado
País o ASN visto por primera vezAADServicePrincipalSignInLogsLocationDetails, AutonomousSystemNumberLa carga de trabajo se autenticó desde una infraestructura que nunca había usado
Nueva credencial en usoAADServicePrincipalSignInLogsServicePrincipalCredentialKeyId, ClientCredentialTypeUna credencial añadida por un atacante, o una rotación no gestionada
Nuevo recurso objetivoAADServicePrincipalSignInLogsResourceDisplayNameLa identidad está accediendo más allá de su función habitual
Credencial añadida al objetoLogs de auditoría de EntraCambios de credencial de aplicación / service principalEl paso de persistencia del Paso 3
Credencial filtrada / inicio de sesión sospechosoDetecciones de identidad de carga de trabajo de ID ProtectionriskyServicePrincipals, servicePrincipalRiskDetectionsCorrelación del lado de Microsoft que usted no tiene que construir

Pico de volumen contra una línea base por identidad

// Workload identity volume spike: last 24h vs the preceding daily baseline
let Recent = 1d;
let Lookback = 30d;
let MinRecent = 20;       // suppress low-volume identities
let MinBaselineDays = 7;  // suppress identities we have not observed long enough
let Baseline =
    AADServicePrincipalSignInLogs
    | where TimeGenerated between (ago(Lookback) .. ago(Recent))
    | where ResultType == "0"
    | summarize Total = count(), Days = dcount(startofday(TimeGenerated)) by AppId
    | where Days >= MinBaselineDays and Total > 0
    | extend BaselineDaily = todouble(Total) / todouble(Days);
AADServicePrincipalSignInLogs
| where TimeGenerated > ago(Recent)
| where ResultType == "0"
| summarize RecentCount = count(), Name = take_any(ServicePrincipalName) by AppId
| where RecentCount >= MinRecent
| join kind=inner (Baseline) on AppId
| extend SpikeRatio = round(RecentCount / BaselineDaily, 1)
| where SpikeRatio >= 5
| project Name, AppId, RecentCount, BaselineDaily = round(BaselineDaily, 1), SpikeRatio
| order by SpikeRatio desc

País o sistema autónomo visto por primera vez

// A workload identity authenticating from a country or ASN it has never used
let Recent = 1d;
let Lookback = 30d;
let Known =
    AADServicePrincipalSignInLogs
    | where TimeGenerated between (ago(Lookback) .. ago(Recent))
    | where ResultType == "0"
    | extend Country = tostring(parse_json(tostring(LocationDetails)).countryOrRegion)
    | where isnotempty(Country)
    | summarize KnownCountries = make_set(Country),
                KnownASNs = make_set(AutonomousSystemNumber) by AppId;
AADServicePrincipalSignInLogs
| where TimeGenerated > ago(Recent)
| where ResultType == "0"
| extend Country = tostring(parse_json(tostring(LocationDetails)).countryOrRegion)
| where isnotempty(Country)
| join kind=inner (Known) on AppId
| where array_length(KnownCountries) > 0
| where not(set_has_element(KnownCountries, Country))
     or not(set_has_element(KnownASNs, AutonomousSystemNumber))
| project TimeGenerated, ServicePrincipalName, AppId, Country,
          ASN = AutonomousSystemNumber, IPAddress, ResourceDisplayName,
          NewCountry = not(set_has_element(KnownCountries, Country))
| order by TimeGenerated desc

Una credencial nunca usada antes

// Sign-in using a credential key this workload identity has never presented
let Recent = 1d;
let Lookback = 30d;
let KnownKeys =
    AADServicePrincipalSignInLogs
    | where TimeGenerated between (ago(Lookback) .. ago(Recent))
    | where ResultType == "0" and isnotempty(ServicePrincipalCredentialKeyId)
    | summarize KnownKeyIds = make_set(ServicePrincipalCredentialKeyId) by AppId;
AADServicePrincipalSignInLogs
| where TimeGenerated > ago(Recent)
| where ResultType == "0" and isnotempty(ServicePrincipalCredentialKeyId)
| join kind=inner (KnownKeys) on AppId
| where not(set_has_element(KnownKeyIds, ServicePrincipalCredentialKeyId))
| project TimeGenerated, ServicePrincipalName, AppId,
          ServicePrincipalCredentialKeyId, ClientCredentialType,
          IPAddress, ResourceDisplayName
| order by TimeGenerated desc

Combine esta última consulta con la entrada del log de auditoría que registra la credencial siendo añadida al objeto de la aplicación o del service principal. Una nueva clave de credencial sin una solicitud de cambio aprobada correspondiente es la señal de mayor fiabilidad de este artículo.

💡

💡 Consejo: Los umbrales anteriores (>= 5x, >= 20 inicios de sesión recientes, >= 7 días de línea base) son barandillas, no física. Cada uno existe para eliminar un falso positivo específico: una integración completamente nueva sin historial mostraría de otro modo un ratio infinito, y un solo inicio de sesión contra una línea base de 0,1/día se leería de otro modo como un pico decuplicado. Ajústelos con sus propios datos antes de alertar.

Dos limitaciones merecen honestidad. Primero, una línea base más corta de lo ideal es inevitable cuando el propio log expira en 7 a 30 días — si no ha exportado a un espacio de trabajo con retención más larga, su línea base está limitada por su licencia. Segundo, un cambio de despliegue legítimo (una nueva región, un agente de build migrado, una rotación planificada) produce exactamente la misma firma que una intrusión. Microsoft reconoce el mismo efecto en su propia detección: "Los inicios de sesión que se inician después de un cambio de configuración autorizado pueden activar esta detección." Estas consultas producen colas de investigación, no veredictos.

La propia detección Suspicious Sign-ins de Microsoft para identidades de carga de trabajo vale la pena activarla junto a estas. "Aprende la línea base del comportamiento de inicio de sesión de las identidades de carga de trabajo en su tenant", tarda "entre 2 y 60 días", y se dispara ante elementos no familiares "dirección IP / ASN, recurso objetivo, user agent, cambio de IP hospedada/no hospedada, país de la IP, tipo de credencial" — el mismo conjunto de características, correlacionado del lado de Microsoft. Los clientes sin Workload Identities Premium "siguen recibiendo todas las detecciones con detalles de informe limitados".

Remediation / Remediación

💡

💡 Victoria rápida: Active la categoría de log de inicio de sesión de service principals en Configuración de diagnóstico y enrútela a un espacio de trabajo de Log Analytics. Sin esto, cada consulta anterior devuelve cero filas — y cero filas son indistinguibles de un tenant sano.

  1. Exporte el flujo y extienda la ventana. Enrute los logs de inicio de sesión de service principals a un espacio de trabajo cuya retención supere el valor predeterminado de 7 o 30 días, para que una línea base sea siquiera posible. Vea Registro de Entra ID: Retención, Configuración de Diagnóstico, Lagunas de Exportación SIEM.

  2. Elimine el secreto en lugar de protegerlo. Donde la carga de trabajo se ejecute en Azure, use identidades administradas para que la plataforma gestione las credenciales. Donde se ejecute fuera de Azure — GitHub Actions, Kubernetes, GCP, AWS — use la federación de identidad de carga de trabajo, que intercambia un token de un proveedor de identidad externo por un token de acceso. El razonamiento de Microsoft es directo: las credenciales almacenadas "suponen un riesgo de seguridad y deben almacenarse de forma segura y rotarse regularmente", mientras que la federación "elimina el riesgo de filtración de secretos o de expiración de certificados". Una credencial que no existe no puede filtrarse.

  3. Delimite una política de Acceso Condicional para identidad de carga de trabajo — correctamente. Cree una política basada en ubicación bajo Usuarios o identidades de carga de trabajo → Identidades de carga de trabajo, apunte a Todos los recursos, incluya Cualquier ubicación y excluya las ubicaciones nombradas desde las que su carga de trabajo se ejecuta legítimamente. Recuerde las tres trampas: requiere Workload Identities Premium, debe asignarse al service principal directamente en lugar de mediante un grupo, y Bloquear el acceso es el único control de concesión. Guárdela primero en modo Solo informe y revise los resultados en la vista Inicios de sesión de service principal antes de aplicarla.

  4. Inventaríe y rote las credenciales. Las recomendaciones de remediación de Microsoft para una identidad de carga de trabajo comprometida son inventariar cada credencial en los objetos de service principal y aplicación, añadir una nueva (certificados x509 preferidos), eliminar las credenciales comprometidas — "Si cree que la cuenta está en riesgo, recomendamos eliminar todas las credenciales existentes" — y rotar cualquier secreto de Key Vault que esa identidad pudiera alcanzar. La higiene de rotación en general se cubre en Entra App Registration: Rotación de Secretos de Credenciales.

  5. Reduzca el radio de impacto antes de necesitarlo. El daño que un secreto filtrado puede causar está limitado por los permisos de aplicación que se le conceden. Elimine las concesiones de Graph a nivel de tenant que no son genuinamente necesarias, y reexamine los service principals que poseen roles de directorio — el lado de la postura estática de este problema se cubre en Rol de Administrador de Service Principal en Entra Sobreprivilegiado.

  6. Verifique. Vuelva a ejecutar las tres consultas. Un tenant correctamente instrumentado devuelve filas correspondientes a sus eventos de despliegue conocidos y nada más; un tenant que no devuelve literalmente nada tiene un problema de registro, no un buen estado de salud. El lugar de esta verificación en una revisión completa del tenant se detalla en Cómo Auditar la Seguridad de Microsoft Entra ID (Azure AD): Guía Práctica de Revisión.

Cómo Detecta Esto EtcSec

El motor de auditoría etc-collector incluye dos detectores que cubren exactamente esta telemetría, ambos calificados como Alto:

  • RISK_SP_SIGNIN_SPIKE — señala un service principal que inició sesión con mucha más frecuencia en las últimas 24 horas que en los días anteriores de la ventana recolectada. La agrupación es por appId sobre tipos de eventos de inicio de sesión de máquina únicamente, de modo que un inicio de sesión de usuario interactivo que porte un appId no puede diluir la tasa medida de una aplicación.
  • RISK_UNUSUAL_GEO_ADMIN — señala una cuenta privilegiada que inició sesión con éxito desde un país que no había usado antes en la ventana recolectada. Este es el gemelo humano de la misma telemetría: la misma lógica de "geografía vista por primera vez" aplicada a administradores en lugar de a cargas de trabajo.

Ambos están construidos alrededor de las barandillas descritas arriba en lugar de un ratio bruto: una identidad con muy poco historial de línea base permanece en silencio, una identidad con muy pocos inicios de sesión recientes permanece en silencio, un conjunto de países conocidos vacío permanece en silencio, y los inicios de sesión sin país resuelto se ignoran en lugar de contarse como un nuevo país. El filtrado por éxito difiere entre ambos: RISK_UNUSUAL_GEO_ADMIN solo cuenta inicios de sesión exitosos, en ambos lados de la comparación, de modo que no se superpone con RISK_FAILED_SIGNIN_BURST, que ya cubre los patrones de intentos fallidos; RISK_SP_SIGNIN_SPIKE mide su tasa sobre cada inicio de sesión de máquina de la ventana, exitoso o fallido.

La ventana reciente está anclada en el evento recolectado más reciente en lugar de en el reloj de pared, de modo que reproducir una auditoría horas después compara los mismos días.

ℹ️

ℹ️ Nota: EtcSec verifica automáticamente esta vulnerabilidad en cada auditoría AD/Azure. Ejecute una auditoría gratuita para verificar su entorno.

Explore las páginas de identidad que apoyan este tema