Entra Service Principal: Detecção de Anomalias de Login e Workload Identity Definida
Entra Service Principal: Detecção de Anomalias de Login, o monitoramento de workload identity que estabelece a baseline de como as contas de máquina se autenticam, é o único controle que a maioria dos tenants nunca configura. Todo outro controle de identidade em um tenant maduro — campanhas de adoção de MFA, métodos de autenticação resistentes a phishing, Conditional Access baseado em risco — é construído em torno de um ser humano presente no teclado. Service principals não têm um humano, e esses controles simplesmente não os alcançam.
A Microsoft é explícita sobre por que essas identidades são diferentes. Uma workload identity é uma identidade que permite que um aplicativo ou service principal acesse recursos, e a própria documentação da Microsoft lista três propriedades que a separam de uma conta de usuário: workload identities "não conseguem realizar autenticação multifator", "muitas vezes não têm um processo formal de ciclo de vida" e "precisam armazenar suas credenciais ou secrets em algum lugar".
Lendo essas três propriedades juntas, a exposição fica óbvia. A credencial é uma string sentada em uma variável de pipeline, um Key Vault, um arquivo appsettings ou uma camada de imagem de container. Não há um segundo fator por trás dela, nenhum ciclo de vida que a expire no último dia de um funcionário que sai, e — como um login de service principal não envolve um usuário — nada que pareça incomum para uma stack de alerta centrada no ser humano. Assim que essa string vaza, o atacante se autentica exatamente como o aplicativo faz, e o login é bem-sucedido na primeira tentativa.
⚠️ Aviso: Depois que um secret de service principal vaza, não há senha para tentar por força bruta, nenhum prompt de MFA para fadigar e nenhum usuário para phishing. O único sinal restante é a forma dos logins: quantos, de onde, em qual rede, com qual credencial.
Por Que Sua Cobertura de MFA e Conditional Access Não Se Aplica
Conditional Access é o controle que os administradores presumem cobrir tudo. Não cobre — workload identities são um dos vários pontos onde sua cobertura silenciosamente termina, ao lado dos erros de escopo e exclusão catalogados em Lacunas do Conditional Access no Entra ID: Quais Configurações Incorretas Deixam Exposição Real. A documentação da Microsoft afirma que as políticas de Conditional Access "historicamente se aplicavam apenas a usuários quando eles acessam apps e serviços", e que o suporte para service principals é uma capacidade separada chamada Conditional Access para workload identities.
Essa capacidade separada traz restrições que decidem se sua política realmente faz alguma coisa:
| Restrição | O que a documentação da Microsoft afirma |
|---|---|
| Licenciamento | "Licenças Workload Identities Premium são necessárias para criar ou modificar políticas de Conditional Access com escopo em service principals." Políticas existentes continuam funcionando em diretórios sem licença, mas não podem ser modificadas. |
| Escopo | "A política pode ser aplicada a service principals de tenant único registrados no seu tenant. Aplicativos SaaS da Microsoft e de terceiros, incluindo apps multi-tenant, não são cobertos por essas políticas. Managed identities não são cobertas pela política." |
| Atribuição a grupos | "Embora service principals possam ser adicionados a grupos, políticas de Conditional Access atribuídas a um grupo que contém um service principal não são aplicadas a esse service principal." Ele precisa ser atribuído diretamente como workload identity. |
| Controles disponíveis | Em Conceder, "Bloquear acesso é a única opção disponível". Não há um "exigir MFA" como alternativa. |
A regra de atribuição a grupos merece destaque, porque ela falha silenciosamente. Um administrador que coloca service principals em um grupo de segurança já alvo de uma política de Conditional Access verá uma política que parece corretamente configurada no portal e não aplica absolutamente nada a esses service principals. Nada dá erro. Nada avisa. A política simplesmente não é avaliada para eles.
As condições suportadas são deliberadamente estreitas: bloquear service principals "fora de intervalos de IP público conhecidos", bloquear "com base no risco detectado pelo Microsoft Entra ID Protection", e contextos de autenticação. Localização e risco — não dispositivo, não MFA, não compliance.
Há também uma assimetria entre detectar risco e bloquear risco, fácil de entender ao contrário. O ID Protection "detecta risco em apps de tenant único, SaaS não Microsoft e multi-tenant", enquanto a aplicação do Conditional Access alcança apenas service principals de tenant único. Managed identities ficam fora de ambos. Assim, para um aplicativo SaaS multi-tenant no seu tenant, você pode ser avisado de que ele está comprometido, mas não consegue bloqueá-lo com uma política de workload identity — o que torna detecção e resposta, não prevenção, o controle operacional.
Onde os Logins de Service Principal Realmente Ficam
O segundo motivo pelo qual esses logins ficam sem observação é que a maioria das queries de hunting nunca os toca. O Microsoft Entra registra quatro tipos de logs de login: logins interativos de usuário, logins não interativos de usuário, logins de service principal e logins de managed identity. Um SOC que construiu suas detecções em cima de logins de usuário não está olhando para um subconjunto filtrado de uma tabela — está olhando para um stream inteiramente diferente.
O stream de service principal é uma coisa à parte. A Microsoft descreve isso claramente: "Diferente dos logins interativos e não interativos de usuário, logins de service principal não envolvem um usuário. Em vez disso, são logins de qualquer conta não-usuário, como apps ou service principals... Nesses logins, o app ou service principal fornece sua própria credencial, como um certificado ou app secret, para se autenticar ou acessar recursos."
Quando exportados para um workspace do Log Analytics via Diagnostic settings, eles caem na tabela AADServicePrincipalSignInLogs — não em SigninLogs. Se você nunca habilitou essa categoria de log, as queries da próxima seção retornam zero linhas, e zero linhas parecem exatamente um ambiente limpo.
A retenção é a outra metade do pré-requisito. A referência de retenção de dados da Microsoft dá aos logs de login sete dias no Microsoft Entra ID Free e 30 dias no P1 ou P2. Uma detecção comportamental precisa de uma baseline mais uma janela recente dentro desse orçamento — você não consegue construir um perfil de 90 dias a partir de um log de 30 dias. Como configurar essa exportação está em Entra ID Logging: Lacunas de Retenção, Diagnostic Settings, Exportação para SIEM; trate isso como um pré-requisito rígido para tudo abaixo.
Estas são as colunas que carregam o sinal:
| Coluna | Por que importa |
|---|---|
AppId, ServicePrincipalName | A identidade a ser baseada. AppId é a chave de agrupamento estável. |
IPAddress, LocationDetails, AutonomousSystemNumber | De onde a workload se autenticou. A rede de uma workload costuma ser muito mais estável que a de um humano. |
ServicePrincipalCredentialKeyId, ClientCredentialType | Qual credencial foi usada, e se era um client secret ou uma client assertion. |
ResourceDisplayName, ResourceIdentity | O que ela acessou. Novos recursos de destino são um sinal forte de pivô. |
ResultType | "0" para sucesso. Falhas são outra investigação. |
ConditionalAccessStatus | Se alguma política de workload identity chegou a ser avaliada. |
FederatedCredentialId | Preenchido quando uma federated identity credential foi usada em vez de um secret armazenado. |
A Cadeia de Ataque Após um Secret Vazar
Passo 1 — A credencial escapa
O secret sai do ambiente: commitado em um repositório público, capturado de um log de build, extraído de uma workstation de desenvolvedor comprometida, ou comprado. A detecção de workload identity Leaked Credentials da Microsoft existe justamente porque isso é rotina — ela verifica credenciais obtidas "do GitHub, da dark web, de paste sites ou de outras fontes" contra credenciais válidas no tenant. O mesmo serviço cobre contas de usuário, onde a falha recorrente está na resposta, não na detecção — veja Entra ID Risk Protection: Credenciais Vazadas, Usuários de Risco Não Corrigidos.
Passo 2 — Uma autenticação que parece totalmente legítima
O atacante executa um fluxo OAuth client credentials com o secret roubado. Nenhuma MFA é possível, então nenhuma está faltando. Se nenhuma política de workload identity estiver diretamente atribuída, ConditionalAccessStatus reflete um login que nada avaliou. O resultado é um login bem-sucedido que é, byte a byte, a mesma operação que o aplicativo real realiza mil vezes por dia. Só o contexto ao redor é diferente.
Passo 3 — Persistência por meio da adição de uma credencial
Em vez de depender de um secret que pode ser rotacionado, um atacante com privilégio suficiente adiciona sua própria credencial ao objeto de aplicativo ou service principal. A identidade agora tem duas credenciais válidas, e revogar a vazada não muda nada. Esse passo deixa dois rastros: uma mudança de diretório no audit log e — em todo login subsequente — um ServicePrincipalCredentialKeyId que nunca apareceu antes para aquele AppId.
Passo 4 — Agindo através do Graph
As permissões de aplicativo do service principal agora são o raio de explosão, e permissões de aplicativo não são limitadas por nenhum direito de usuário. Concessões em todo o tenant, como Directory.ReadWrite.All ou RoleManagement.ReadWrite.Directory, transformam um secret vazado em controle total do diretório; veja Permissões Perigosas do Graph API em Registro de Aplicativo Entra para como essas concessões são inventariadas e reduzidas. A detecção Suspicious API Traffic da Microsoft mira exatamente nesse estágio, disparando "quando tráfego anormal de GraphAPI ou enumeração de diretório de um service principal é observado".
Detecção de Anomalias de Login: O Que Extrair dos Logs
A detecção comportamental aqui se apoia em uma propriedade que favorece o defensor: workloads são chatamente consistentes. Um job de sincronização noturno roda na mesma hora, do mesmo IP de saída, contra o mesmo recurso, com a mesma credencial. Humanos são erráticos; automação não é. Essa consistência torna o desvio significativo — e é por isso que as detecções equivalentes para contas de usuário, como viagem impossível e replay de token AiTM, precisam tolerar muito mais ruído do que as queries abaixo.
| Indicador | Onde procurar | Campos-chave | O que significa |
|---|---|---|---|
| Pico de volume de login | AADServicePrincipalSignInLogs | AppId, contagem ao longo do tempo | Coleta em massa ou enumeração através de um token roubado |
| País ou ASN visto pela primeira vez | AADServicePrincipalSignInLogs | LocationDetails, AutonomousSystemNumber | A workload se autenticou a partir de infraestrutura que nunca usou |
| Nova credencial em uso | AADServicePrincipalSignInLogs | ServicePrincipalCredentialKeyId, ClientCredentialType | Uma credencial adicionada por um atacante, ou uma rotação não gerenciada |
| Novo recurso de destino | AADServicePrincipalSignInLogs | ResourceDisplayName | A identidade está acessando além do seu trabalho normal |
| Credencial adicionada ao objeto | Audit logs do Entra | Mudanças de credencial de aplicativo / service principal | O passo de persistência do Passo 3 |
| Credencial vazada / login suspeito | Detecções de workload identity do ID Protection | riskyServicePrincipals, servicePrincipalRiskDetections | Correlação feita pela Microsoft, que você não precisa construir |
Pico de volume contra uma baseline por identidade
// 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 ou sistema autônomo visto pela primeira 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
Uma credencial que nunca foi 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 essa última query com a entrada de audit log que registra a credencial sendo adicionada ao objeto de aplicativo ou service principal. Uma nova credential key sem uma change request correspondente e aprovada é o sinal de maior confiança neste artigo.
💡 Dica: Os limites acima (>= 5x, >= 20 logins recentes, >= 7 dias de baseline) são guardrails, não física. Cada um existe para eliminar um falso positivo específico: uma integração recém-criada, sem histórico, mostraria de outra forma uma proporção infinita, e um único login contra uma baseline de 0,1/dia pareceria, de outra forma, um pico de dez vezes. Ajuste-os aos seus próprios dados antes de alertar.
Duas limitações merecem honestidade. Primeiro, uma baseline mais curta do que o ideal é inevitável quando o próprio log expira em 7 a 30 dias — se você não exportou para um workspace com retenção maior, sua baseline está limitada pela sua licença. Segundo, uma mudança de deployment legítima (uma nova região, um build agent migrado, uma rotação planejada) produz exatamente a mesma assinatura de uma intrusão. A própria Microsoft reconhece esse efeito em sua detecção: "Logins iniciados após uma mudança de configuração autorizada podem disparar essa detecção." Essas queries produzem filas de investigação, não vereditos.
Vale a pena habilitar também a detecção Suspicious Sign-ins da própria Microsoft para workload identities. Ela "aprende a baseline de comportamento de login para workload identities no seu tenant", levando "entre 2 e 60 dias", e dispara em "endereço IP/ASN, recurso de destino, user agent, mudança entre IP de hosting/não-hosting, país do IP, tipo de credencial" desconhecidos — o mesmo conjunto de características, correlacionado pela própria Microsoft. Clientes sem Workload Identities Premium "ainda recebem todas as detecções, com detalhes de relatório limitados".
Correção
💡 Vitória Rápida: Habilite a categoria de log de login de service principal em Diagnostic settings e envie-a para um workspace do Log Analytics. Sem isso, toda query acima retorna zero linhas — e zero linhas são indistinguíveis de um tenant saudável.
-
Exporte o stream e estenda a janela. Envie os logs de login de service principal para um workspace cuja retenção exceda o padrão de 7 ou 30 dias, para que uma baseline seja sequer possível. Veja Entra ID Logging: Lacunas de Retenção, Diagnostic Settings, Exportação para SIEM.
-
Remova o secret em vez de protegê-lo. Onde a workload roda no Azure, use managed identities para que a plataforma gerencie as credenciais. Onde ela roda fora do Azure — GitHub Actions, Kubernetes, GCP, AWS — use workload identity federation, que troca um token de um identity provider externo por um access token. A justificativa da Microsoft é direta: credenciais armazenadas "representam um risco de segurança e precisam ser armazenadas com segurança e rotacionadas regularmente", enquanto a federation "elimina o risco de vazar secrets ou de certificados expirarem". Uma credencial que não existe não pode vazar.
-
Configure uma política de Conditional Access para workload identity — corretamente. Crie uma política baseada em localização em Usuários ou workload identities → Workload identities, tenha como alvo Todos os recursos, inclua Qualquer localização e exclua os locais nomeados de onde sua workload legitimamente opera. Lembre-se das três armadilhas: exige Workload Identities Premium, precisa ser atribuída ao service principal diretamente, e não via grupo, e Bloquear acesso é o único controle de concessão. Salve-a primeiro em modo somente relatório e leia os resultados na visualização Logins de service principal antes de aplicar.
-
Inventarie e rotacione credenciais. A orientação de remediação da Microsoft para uma workload identity comprometida é inventariar cada credencial tanto no service principal quanto no objeto de aplicativo, adicionar uma nova (certificados x509 preferidos), remover as credenciais comprometidas — "se você acredita que a conta está em risco, recomendamos remover todas as credenciais existentes" — e rotacionar quaisquer secrets do Key Vault que essa identidade pudesse alcançar. A higiene de rotação em geral é abordada em Rotação de Credenciais em Registro de Aplicativo Entra Secrets.
-
Reduza o raio de explosão antes de precisar. O dano que um secret vazado pode causar é limitado pelas permissões de aplicativo concedidas a ele. Remova concessões amplas do Graph em todo o tenant que não sejam genuinamente necessárias, e reexamine service principals que detêm funções de diretório — o lado estático desse problema é abordado em Service Principal Entra com Privilégios de Admin Excessivos.
-
Verifique. Execute novamente as três queries. Um tenant corretamente instrumentado retorna linhas para seus eventos de deployment conhecidos e nada mais; um tenant que retorna literalmente nada tem um problema de logging, não um atestado de saúde. Onde essa verificação se encaixa em uma revisão completa do tenant está descrito em Como Auditar a Segurança do Microsoft Entra ID (Azure AD): Guia Prático.
Como o EtcSec Detecta Isso
O motor de auditoria etc-collector traz dois detectores que cobrem exatamente essa telemetria, ambos classificados como Alto:
RISK_SP_SIGNIN_SPIKE— sinaliza um service principal que fez login com frequência muito maior nas últimas 24 horas do que nos dias anteriores da janela coletada. O agrupamento é feito porappId, apenas sobre tipos de evento de login de máquina, de modo que um login interativo de usuário que por acaso carrega umappIdnão pode diluir a taxa medida de um aplicativo.RISK_UNUSUAL_GEO_ADMIN— sinaliza uma conta privilegiada que fez login com sucesso a partir de um país que não havia usado antes na janela coletada. Este é o equivalente humano da mesma telemetria: a mesma lógica de "geografia vista pela primeira vez" aplicada a admins em vez de workloads.
Ambos são construídos em torno dos guardrails descritos acima, em vez de uma proporção bruta: uma identidade com pouco histórico de baseline fica silenciosa, uma identidade com poucos logins recentes fica silenciosa, um conjunto vazio de países conhecidos fica silencioso, e logins sem país resolvido são ignorados em vez de contados como um novo país. A filtragem de sucesso difere entre os dois: RISK_UNUSUAL_GEO_ADMIN conta apenas logins bem-sucedidos, dos dois lados da divisão, então não se sobrepõe a RISK_FAILED_SIGNIN_BURST, que já cobre padrões de tentativas falhas; RISK_SP_SIGNIN_SPIKE mede sua taxa sobre todo login de máquina na janela, bem-sucedido ou falho.
A janela recente é ancorada no evento coletado mais recente, em vez do horário do relógio, de modo que reproduzir uma auditoria horas depois compara os mesmos dias.
ℹ️ Nota: O EtcSec verifica automaticamente essa vulnerabilidade em toda auditoria AD/Azure. Execute uma auditoria gratuita para verificar seu ambiente.
Explore as paginas de identidade que sustentam este tema
