Home Microsoft GraphMicrosoft Graph na prática: investigando privilégios, aplicações e riscos de identidade

Microsoft Graph na prática: investigando privilégios, aplicações e riscos de identidade

por Cropped josimarhedler.pngJosimar Hedler
3 visualizações 26 minutos leitura

No primeiro artigo desta série, percorremos a superfície de identidade do Microsoft Graph: usuários, grupos, aplicações, Service Principals, Directory Roles, PIM, Conditional Access, Identity Protection, sign-in logs e audit logs, cada área foi apresentada isoladamente, com um cmdlet representativo.

Isso é um bom ponto de partida, mas não é onde uma investigação real termina, quando o time de segurança precisa responder a uma pergunta concreta, raramente basta um único comando, apergunta deixa de ser “qual comando lista usuários?” e passa a ser “como descobrir se uma identidade representa risco?”, essa segunda pergunta não tem resposta em um objeto isolado, ela normalmente exige atravessar vários deles, na sequência aproximada:

Usuário
   ↓
Grupos
   ↓
Directory Roles
   ↓
PIM
   ↓
Aplicações acessadas
   ↓
Sign-ins
   ↓
Risco

Ou, em um cenário de investigação de aplicação:

Application
   ↓
Service Principal
   ↓
Owners
   ↓
Credentials
   ↓
API permissions
   ↓
Consent
   ↓
Sign-ins

A ideia central deste artigo é simples de enunciar e mais difícil de praticar, o valor do Microsoft Graph não está apenas em consultar objetos isolados, mas em relacionar diferentes sinais de identidade.

Da consulta para a investigação

Comandos como Get-MgUser, Get-MgGroup ou Get-MgApplication respondem perguntas individuais, quem é esse usuário, quem está nesse grupo, o que é essa aplicação. Mas segurança de identidade, na prática, é feita de perguntas compostas:

  • Quem possui privilégios administrativos?
  • Esse privilégio é permanente ou elegível via PIM?
  • Quem é responsável por determinada aplicação?
  • Essa aplicação possui Client Secrets? Quando expiram?
  • Quais permissões essa aplicação recebeu? Houve Admin Consent?
  • Esse Service Principal está realmente sendo utilizado?
  • Existem usuários de risco? Houve autenticação suspeita?
  • Qual aplicação participou daquele login?

Nenhuma dessas perguntas é respondida por um único cmdlet, todas seguem o mesmo padrão de investigação:

Objeto → Relacionamento → Contexto → Evidência → Risco

O restante deste artigo constrói duas investigações completas seguindo esse padrão, uma partindo de um usuário e outra partindo de uma aplicação, e depois mostra como consolidar os sinais.

Construindo uma investigação de identidade

Cenário: o time de segurança recebeu um alerta genérico envolvendo a conta [email protected] e precisa responder:

  1. A conta está habilitada?
  2. Quais grupos ela integra?
  3. Possui alguma Directory Role?
  4. Possui role elegível pelo PIM?
  5. Houve logins recentes?
  6. Existe risco associado à identidade?
  7. Quais aplicações aparecem em suas autenticações?

Não existe um único cmdlet que responda às sete perguntas de forma completa, é perfeitamente possível escrever um script que execute todas as consultas e consolide o resultado, e é exatamente isso que equipes maduras fazem, o ponto aqui é outro, cada pergunta empurra o investigador para uma área diferente do Graph, e é preciso correlacionar recursos distintos para chegar à resposta:

Users
   ↓
Groups
   ↓
Directory Roles
   ↓
PIM
   ↓
Sign-in Logs
   ↓
Identity Protection

Cada etapa envolve módulo, cmdlet, endpoint e permissão próprios, é essa fragmentação, e não a dificuldade de cada comando individual, que torna a investigação trabalhosa.

Começando pelo usuário

O ponto de entrada é Get-MgUser, do módulo Microsoft.Graph.Users, consultando o endpoint /users.

$user = Get-MgUser -UserId "[email protected]" `
    -Property Id, DisplayName, UserPrincipalName, AccountEnabled, UserType, CreatedDateTime

$user | Select-Object Id, DisplayName, UserPrincipalName, AccountEnabled, UserType, CreatedDateTime

A permissão delegada com menor privilégio para essa consulta é User.Read.All, a propriedade UserType já responde a uma pergunta relevante: se a conta é Member (colaborador interno) ou Guest (identidade externa convidada), guests costumam merecer atenção adicional em atividades privilegiadas.

O campo mais importante para o restante da investigação, porém, nem aparece em destaque, é o Id do objeto, que conecta essa consulta a todas as próximas, por isso o resultado fica guardado em $user, e o $user.Id será reaproveitado nos exemplos seguintes.

Grupos como camada de autorização

A partir do Id do usuário, o próximo passo é entender a quais grupos ele pertence, o cmdlet Get-MgUserMemberOf, também do módulo Microsoft.Graph.Users, retorna essa relação diretamente do objeto de usuário.

$memberOf = Get-MgUserMemberOf -UserId $user.Id -All
$memberOf | Select-Object Id, @{n='Tipo';e={$_.AdditionalProperties['@odata.type']}}, @{n='Nome';e={$_.AdditionalProperties['displayName']}}

Como o cenário é o de um investigador consultando outro usuário (e não o próprio perfil autenticado), a permissão delegada com menor privilégio é User.Read.All, no contexto de aplicação (app-only), a permissão mínima é Directory.Read.All.

O retorno inclui grupos de segurança, Microsoft 365 Groups e, em alguns casos, o próprio objeto de Directory Role, o que já antecipa a próxima seção.

Atenção: Get-MgUserMemberOf retorna apenas a associação direta, grupos aninhados exigiriam avaliar a cadeia de forma transitiva (Get-MgUserTransitiveMemberOf), o que este artigo não vai aprofundar, pertencer a um grupo não significa automaticamente possuir privilégio administrativo, grupos são usados como mecanismo de autorização em cenários muito diferentes: licenciamento, escopo de Conditional Access, associação de PIM, permissão em uma aplicação corporativa. O significado de um grupo depende do que o consome, não da associação em si.

Privilégios no Microsoft Entra ID

O passo seguinte é verificar se a conta está associada a uma Microsoft Entra Directory Role, o módulo responsável é Microsoft.Graph.Identity.DirectoryManagement.

Primeiro, listar as roles ativadas no tenant:

Get-MgDirectoryRole | Select-Object Id, DisplayName, RoleTemplateId

Em seguida, para uma role específica, consultar seus membros:

$role = Get-MgDirectoryRole -Filter "DisplayName eq 'Application Administrator'"
Get-MgDirectoryRoleMember -DirectoryRoleId $role.Id

A permissão delegada com menor privilégio para listar roles é RoleManagement.Read.Directory, a mesma exigida para consultar membros de uma role, a roles como Global Administrator, Privileged Role Administrator, Application Administrator e Cloud Application Administrator concedem capacidades administrativas amplas sobre o diretório e sobre serviços Microsoft 365.

Atenção: Microsoft Entra Directory Roles não são Azure RBAC, directory Roles administram objetos e serviços do tenant Microsoft Entra ID e Microsoft 365: usuários, grupos, aplicações, políticas, Azure RBAC controla acesso a recursos de infraestrutura Azure por meio do Azure Resource Manager, com modelo de atribuição, escopo e API completamente diferentes, este artigo não utiliza cmdlets do módulo Az em nenhum momento.

O privilégio é permanente ou elegível?

Get-MgDirectoryRoleMember mostra apenas quem está atualmente atribuído a uma role, isso é uma fotografia parcial: em tenants que usam PIM, boa parte das atribuições privilegiadas não é permanente, são elegíveis, ativadas apenas quando necessário, por tempo limitado.

O módulo Microsoft.Graph.Identity.Governance permite investigar esse estado, como o cenário é a conta da Ana, o ideal é filtrar pelo PrincipalId dela, em vez de listar o tenant inteiro. Para atribuições elegíveis:

Get-MgRoleManagementDirectoryRoleEligibilityScheduleInstance `
    -Filter "principalId eq '$($user.Id)'" `
    -ExpandProperty RoleDefinition |
    Select-Object PrincipalId,
        @{n='Role';e={$_.RoleDefinition.DisplayName}},
        DirectoryScopeId, StartDateTime, EndDateTime

Para atribuições atualmente ativas:

Get-MgRoleManagementDirectoryRoleAssignmentScheduleInstance `
    -Filter "principalId eq '$($user.Id)'" `
    -ExpandProperty RoleDefinition |
    Select-Object PrincipalId,
        @{n='Role';e={$_.RoleDefinition.DisplayName}},
        AssignmentType, DirectoryScopeId, StartDateTime, EndDateTime

A permissão delegada com menor privilégio documentada para consultar elegibilidade é RoleEligibilitySchedule.Read.Directory; para atribuições ativas, o par equivalente é RoleAssignmentSchedule.Read.Directory.

O ciclo de vida de uma atribuição elegível, em linhas gerais:

Eligible
   ↓
Activation
   ↓
Active
   ↓
Activation expires
   ↓
Eligible

Vale registrar que também podem existir Active assignments diretas, permanentes ou com prazo determinado, que nascem já ativas, sem passar por um estado Eligible prévio nem pelo ciclo de ativação do PIM, a propriedade AssignmentType ajuda a separar os casos: Activated indica uma ativação feita a partir de uma elegibilidade, enquanto Assigned indica uma atribuição ativa direta.

Este artigo não é um tutorial de configuração de PIM, o objetivo é mais restrito: mostrar que o Graph permite investigar o estado atual de privilégios, não apenas quem está com uma role ligada permanentemente, um inventário que enxerga só atribuições ativas pode não revelar todas as identidades com capacidade de obter um privilégio a qualquer momento, a elegibilidade é, ela mesma, um sinal de exposição.

Privilégios herdados por grupos

As consultas acima mostram apenas atribuições feitas diretamente ao usuário, uma investigação completa precisa considerar também privilégios que chegam por intermédio de grupos, por dois caminhos:

1. Grupos com Directory Roles atribuídas. Roles só podem ser atribuídas a grupos marcados como role-assignable, a partir dos grupos do usuário (já obtidos em $memberOf), é possível verificar se algum deles possui atribuição de role:

$groupIds = $memberOf |
    Where-Object { $_.AdditionalProperties['@odata.type'] -eq '#microsoft.graph.group' } |
    Select-Object -ExpandProperty Id

foreach ($gid in $groupIds) {
    Get-MgRoleManagementDirectoryRoleAssignment `
        -Filter "principalId eq '$gid'" `
        -ExpandProperty RoleDefinition |
        Select-Object PrincipalId, @{n='Role';e={$_.RoleDefinition.DisplayName}}, DirectoryScopeId
}

Se um grupo desses tiver atribuições via PIM, as mesmas consultas de EligibilityScheduleInstance e AssignmentScheduleInstance mostradas acima também se aplicam ao PrincipalId do grupo.

2. PIM for Groups. O próprio acesso do usuário ao grupo pode ser elegível, ativado sob demanda, nesse caso a associação não aparece em Get-MgUserMemberOf enquanto não estiver ativa:

# Elegibilidade para membership/ownership de grupos
Get-MgIdentityGovernancePrivilegedAccessGroupEligibilityScheduleInstance `
    -Filter "principalId eq '$($user.Id)'" |
    Select-Object GroupId, PrincipalId, AccessId, StartDateTime, EndDateTime

# Ativações e atribuições ativas em grupos
Get-MgIdentityGovernancePrivilegedAccessGroupAssignmentScheduleInstance `
    -Filter "principalId eq '$($user.Id)'" |
    Select-Object GroupId, PrincipalId, AccessId, AssignmentType, StartDateTime, EndDateTime

As permissões delegadas mínimas são PrivilegedEligibilitySchedule.Read.AzureADGroup e PrivilegedAssignmentSchedule.Read.AzureADGroup, respectivamente, o AccessId indica se o acesso é como member ou owner do grupo.

O encadeamento completo, portanto, pode ser mais longo do que parece à primeira vista:

Usuário → (elegível em) Grupo → (atribuído a) Directory Role

Um usuário sem nenhuma role direta pode, ainda assim, alcançar um privilégio administrativo por esse caminho, §um exemplo claro de como uma resposta incompleta em uma etapa leva a uma conclusão errada na seguinte.

Segunda investigação: uma aplicação

Muda-se agora o objeto de partida. Cenário: existe uma aplicação chamada FinanceAutomation e o time de identidade precisa responder:

  • Existe um Application Object e qual Service Principal a representa no tenant?
  • Quem são os owners?
  • Existem Client Secrets ou certificados, e quando expiram?
  • Quais permissões Microsoft Graph foram concedidas, e houve Admin Consent?
  • A aplicação apresenta atividade recente?

Antes de qualquer cmdlet, vale reforçar uma distinção que este artigo trata como não negociável:

Application Object ≠ Service Principal.

O Application Object é a definição da aplicação, o registro, único no tenant onde foi criado, a Service Principal é a instância dessa aplicação em tempo de execução dentro de um tenant específico, é o objeto que efetivamente recebe permissões e aparece em sign-ins, uma aplicação multi-tenant tem um único Application Object no tenant de origem, mas um Service Principal distinto em cada tenant onde foi consentida.

# Application Object
$app = Get-MgApplication -Filter "DisplayName eq 'FinanceAutomation'"

# Service Principal correspondente no tenant
$sp = Get-MgServicePrincipal -Filter "AppId eq '$($app.AppId)'"

Ambos os cmdlets pertencem ao módulo Microsoft.Graph.Applications, e a permissão delegada com menor privilégio para ambos é Application.Read.All.

Owners: quem responde pela identidade?

Owners podem ser consultados tanto no Application Object quanto no Service Principal:

Get-MgApplicationOwner -ApplicationId $app.Id
Get-MgServicePrincipalOwner -ServicePrincipalId $sp.Id

Os dois cmdlets são do módulo Microsoft.Graph.Applications, com Application.Read.All como permissão delegada mínima em ambos os casos.

Owner é um sinal de governança, não uma medida direta de risco, vale levantar a aplicação não tem nenhum owner cadastrado? O owner listado já saiu da organização? Existe apenas um, sem redundância? É uma conta de serviço, e não uma pessoa identificável? Nenhuma dessas condições, isoladamente, indica um incidente, elas indicam onde a responsabilidade operacional pela aplicação pode estar mal definida, o que importa tanto para incidentes quanto para o dia a dia, como decidir quem aprova uma renovação de credencial.

Client Secrets e certificados

Uma aplicação não se autentica necessariamente só por Client Secret ou certificado, o Microsoft Graph também suporta Federated Identity Credentials (federatedIdentityCredentials), o mecanismo por trás de workload identity federation, que permite trocar um token emitido por um provedor de identidade externo confiável por um token de acesso, sem que nenhum segredo precise ser armazenado, este artigo mantém o foco em passwordCredentials e keyCredentials, que continuam sendo, hoje, o par mais comum em tenants estabelecidos, mas federatedIdentityCredentials é um terceiro objeto de credencial, igualmente investigável, exposto pelo cmdlet Get-MgApplicationFederatedIdentityCredential, do mesmo módulo Microsoft.Graph.Applications.

Client Secret (passwordCredentials) e certificado (keyCredentials) são propriedades do Application Object e podem ser recuperadas com Get-MgApplication, selecionando explicitamente essas propriedades:

$creds = Get-MgApplication -ApplicationId $app.Id -Property PasswordCredentials, KeyCredentials

# Client Secrets
$creds.PasswordCredentials | Select-Object DisplayName, StartDateTime, EndDateTime, KeyId

# Certificados
$creds.KeyCredentials | Select-Object DisplayName, StartDateTime, EndDateTime, KeyId, Type, Usage

Um ponto que costuma gerar confusão, o Microsoft Graph nunca retorna novamente o valor de um Client Secret depois de criado, nem por API, nem pelo portal, ficam disponíveis apenas os metadados: nome, identificador (KeyId) e datas de início e expiração. É suficiente para responder o que importa numa investigação: existe credencial já expirada? Alguma expirando em breve? Há múltiplos secrets ativos sem justificativa clara? Alguma tem vida útil incomumente longa?

Vale resistir à tentação de tratar todo secret como vulnerabilidade por padrão, o risco real depende da política da organização, de quem acessa o valor e de como é usado, quando o cenário permite, a ordem geral de preferência é:

  1. Federated Identity Credentials (workload identity federation): o fluxo não depende de credencial persistente armazenada pela workload, porque o token do provedor externo confiável é trocado por um token de acesso.
  2. Certificados: reduzem a dependência de Client Secrets, mas não eliminam o trabalho, a chave privada continua precisando de proteção, controle de acesso e renovação periódica.
  3. Client Secrets: a opção mais frágil para automações sensíveis, por serem uma string que precisa ser armazenada, protegida e rotacionada manualmente.

O tema merece um artigo próprio, não aprofundamento aqui.

API permissions e consentimento

Esta é provavelmente a área mais mal compreendida da investigação de aplicações, e a mais importante.

O primeiro ponto a esclarecer é que existem dois tipos de permissão completamente diferentes:

  • Delegated permissions: a aplicação atua em nome de um usuário autenticado; o acesso efetivo é a interseção entre o que a permissão delega e o que o usuário poderia fazer.
  • Application permissions: a aplicação atua com identidade própria, sem usuário associado, o acesso tende a corresponder ao que a permissão concede, independentemente de quem (ou se alguém) está autenticado, embora fatores como escopo administrativo e políticas específicas do tenant possam restringir esse alcance na prática.

Essa diferença muda completamente a análise de risco, uma Application permission mal escopada é mais preocupante, porque não depende de nenhum usuário estar logado para ser exercida.

O segundo ponto é a distinção entre permissão solicitada e permissão concedida, o Application Object possui a propriedade requiredResourceAccess, que representa as permissões que a aplicação declara precisar. É uma solicitação, definida em tempo de design/registro da aplicação, e não implica que o acesso já exista, as permissões efetivamente concedidas aparecem como registros relacionados ao Service Principal, em dois objetos distintos:

  • OAuth2PermissionGrant: representa permissões delegadas efetivamente concedidas (consentimento).
  • AppRoleAssignment: representa application permissions efetivamente concedidas.
# Permissões delegadas concedidas (consentimento)
Get-MgServicePrincipalOauth2PermissionGrant -ServicePrincipalId $sp.Id

# Permissões de aplicação concedidas a este Service Principal
Get-MgServicePrincipalAppRoleAssignment -ServicePrincipalId $sp.Id

Get-MgServicePrincipalOauth2PermissionGrant tem como permissão delegada mínima Directory.Read.All; Get-MgServicePrincipalAppRoleAssignment tem Application.Read.All. Ambos pertencem ao módulo Microsoft.Graph.Applications.

Service Principal
        ↓
Microsoft Graph
        ↓
Application Permission
        ↓
Admin Consent

Perguntas investigativas típicas nessa etapa, esta aplicação possui permissões do tipo Application (não apenas Delegated)? Consegue ler usuários, grupos ou acessar o diretório de forma ampla? Existe consentimento administrativo registrado, ou o consentimento é apenas de usuário?

Um cuidado importante: não classifique uma permissão como “crítica” apenas pelo nome. Directory.Read.All soa amplo, mas é leitura; Mail.Send soa inofensivo, mas permite enviar e-mail em nome de qualquer usuário se for do tipo Application, o impacto real depende do escopo (delegated vs. application) e do contexto de uso, não do quão assustador o nome parece.

A aplicação está sendo utilizada?

Até aqui, a investigação respondeu “o que essa aplicação pode fazer”: owners, credenciais, permissões, falta responder “o que ela está de fato fazendo”.

O Microsoft Graph expõe um recurso dedicado para isso, o servicePrincipalSignInActivity, mas ele está disponível apenas em beta: não existe equivalente em v1.0 no momento da publicação deste artigo. O cmdlet correspondente também é beta:

# BETA: requer o módulo Microsoft.Graph.Beta.Reports
Get-MgBetaReportServicePrincipalSignInActivity -Top 20 |
    Select-Object AppId, LastSignInActivity

A permissão delegada mínima documentada é AuditLog.Read.All. Por estar em beta, o comportamento e o schema desse recurso podem mudar sem o mesmo compromisso de estabilidade da v1.0, trate-o como apoio de investigação pontual, não como base de uma automação de produção.

Correlacionando o que já foi levantado (Service Principal → Permission → Credential → Sign-in activity), dá para diferenciar duas afirmações que parecem sinônimas, mas não são: “possui capacidade” e “está utilizando essa capacidade”, uma aplicação pode ter Application permissions amplas concedidas há anos e nunca ter feito uma única autenticação, sinal relevante para revisão de limpeza, mas ausência de registros de sign-in não prova definitivamente que a aplicação não está em uso: retenção de dados, período consultado e licenciamento do tenant afetam o que fica disponível, trate a ausência de evidência como indício a validar, não como conclusão fechada.

Usuários de risco e Identity Protection

Voltando à investigação de usuário: o módulo Microsoft.Graph.Identity.SignIns expõe o recurso de Identity Protection.

Get-MgRiskyUser -Filter "userPrincipalName eq '[email protected]'" |
    Select-Object UserPrincipalName, RiskLevel, RiskState, RiskDetail

A permissão delegada mínima é IdentityRiskyUser.Read.All, e o recurso requer licenciamento Microsoft Entra ID P2 no tenant.

Vale diferenciar dois conceitos usados como sinônimos sem ser: Risky User é um estado acumulado sobre a identidade, calculado a partir de múltiplos sinais ao longo do tempo; sign-in risk é uma avaliação pontual, associada a uma tentativa de autenticação específica. Um usuário pode ter um sign-in de risco isolado sem virar Risky User, e o inverso também ocorre, o Identity Protection combina sinais e modelos de detecção do Microsoft Entra ID; a interpretação de RiskLevel e RiskState depende do contexto da organização, não é um veredito automático.

Sign-in logs como evidência

Fechando o ciclo da investigação de usuário, o módulo Microsoft.Graph.Reports permite consultar os registros de autenticação.

Get-MgAuditLogSignIn -Filter "userPrincipalName eq '[email protected]'" -Top 20 |
    Select-Object CreatedDateTime, UserPrincipalName, AppDisplayName, IpAddress,
        ClientAppUsed, ConditionalAccessStatus, Status

A permissão delegada mínima é AuditLog.Read.All,com isso, é possível reconstruir a cadeia completa:

Usuário → Sign-in → Aplicação → IP → Cliente → Conditional Access → Resultado

Os campos DeviceDetail e Location também existem no objeto de sign-in e podem entrar na mesma consulta quando o cenário exigir correlação com dispositivo ou geografia; foram omitidos aqui só para manter o exemplo enxuto.

Vale registrar que Get-MgAuditLogSignIn, em v1.0, não suporta filtrar por signInEventTypes para isolar autenticações de Service Principal; esse filtro só existe na versão beta (Get-MgBetaAuditLogSignIn). Para investigação de usuário isso não é obstáculo. É uma limitação a lembrar caso a investigação migre para sign-ins de aplicação, o Graph também expõe, no mesmo módulo, Audit Logs (mudanças administrativas) e Provisioning Logs (provisionamento automatizado), complementares aos sign-in logs.

Vale registrar também que a consulta e o download dos sign-in logs via Microsoft Graph requerem licenciamento Microsoft Entra ID P1 ou P2 no tenant, conforme a documentação oficial.

Correlacionando os sinais

Nenhuma das consultas apresentadas até aqui, isoladamente, prova um incidente, um secret expirando não é um ataque, um owner ausente não é uma invasão, um sign-in de um IP incomum não é, por si só, comprometimento, é a correlação entre os sinais que cria contexto.

PerguntaOrigem no GraphObjeto
Quem é a identidade?Microsoft.Graph.Usersuser
De quais grupos participa?Microsoft.Graph.Users / Groupsgroup
Possui privilégio administrativo?Microsoft.Graph.Identity.DirectoryManagementdirectoryRole
Pode ativar uma role?Microsoft.Graph.Identity.GovernanceroleEligibilityScheduleInstance
Pode ativar acesso a um grupo privilegiado?Microsoft.Graph.Identity.GovernanceprivilegedAccess groupEligibilityScheduleInstance
Existe risco associado?Microsoft.Graph.Identity.SignInsriskyUser
Quando autenticou?Microsoft.Graph.ReportssignIn
Quem responde pela aplicação?Microsoft.Graph.Applicationsowner
A aplicação possui credenciais?Microsoft.Graph.ApplicationspasswordCredentials / keyCredentials / federatedIdentityCredentials
Quais permissões foram concedidas?Microsoft.Graph.Applicationsoauth2PermissionGrant / appRoleAssignment
Existe atividade?Microsoft.Graph.Reports (v1.0) / Beta (Service Principal)signIn / servicePrincipalSignInActivity

Essa tabela resume, em uma única visão, os dois fluxos de investigação construídos ao longo do artigo: o de usuário (Users → Groups → Directory Roles → PIM → Risk → Sign-ins) e o de aplicação (Application → Service Principal → Owners → Credentials → Permissions → Consent → Activity).

Filter, Select e paginação

Um detalhe prático que separa um script de laboratório de um script que funciona em um tenant real: a filtragem deve acontecer no servidor sempre que possível, não localmente depois de baixar tudo, -Filter aplica um filtro OData no próprio Graph; -Property reduz o payload às propriedades usadas; -Top limita resultados por página; -All percorre a paginação automaticamente.

# Preferível: filtra no servidor
Get-MgUser -Filter "accountEnabled eq false" -Property Id, DisplayName -Top 50

# Evitar em tenants grandes: baixa tudo e filtra localmente
Get-MgUser -All | Where-Object { $_.AccountEnabled -eq $false }

Essa diferença deixa de ser estilo de código quando o tenant tem dezenas ou centenas de milhares de objetos, baixar tudo para filtrar localmente significa tráfego, tempo de execução e throttling desnecessários, para investigação em escala, -Filter e -Property deveriam ser hábito, não exceção, os exemplos de PIM deste artigo seguem a mesma lógica: filtrar por principalId no servidor, em vez de listar todas as atribuições do tenant.

Permissões: Least Privilege na prática

Consolidando as permissões utilizadas nos exemplos deste artigo:

OperaçãoPermissão Graph mínima recomendada
Ler dados do usuárioUser.Read.All
Listar grupos de um usuárioUser.Read.All (delegated) / Directory.Read.All (app-only)
Listar Directory Roles e seus membrosRoleManagement.Read.Directory
Consultar elegibilidade PIM (roles)RoleEligibilitySchedule.Read.Directory
Consultar atribuições ativas PIM (roles)RoleAssignmentSchedule.Read.Directory
Consultar elegibilidade PIM for GroupsPrivilegedEligibilitySchedule.Read.AzureADGroup
Consultar atribuições ativas PIM for GroupsPrivilegedAssignmentSchedule.Read.AzureADGroup
Ler Application/Service Principal, owners e AppRoleAssignmentApplication.Read.All
Ler credenciais federadas (federatedIdentityCredentials)Application.Read.All
Ler consentimento delegado (OAuth2PermissionGrant)Directory.Read.All
Consultar usuários de risco (requer Entra ID P2)IdentityRiskyUser.Read.All
Consultar sign-in logs (requer Entra ID P1/P2)AuditLog.Read.All
Sign-in de Service Principal (BETA)AuditLog.Read.All

Importante: A permissão Graph não é a única porta, tabela acima mostra o escopo que o token precisa ter, mas isso nem sempre é suficiente, em acesso delegado, o Graph avalia duas coisas, o escopo consentido para a aplicação (por exemplo, o app do Microsoft Graph PowerShell) e as permissões efetivas do usuário autenticado, muitas das APIs deste artigo exigem que quem está executando tenha também uma Microsoft Entra Role compatível (como Global Reader, Security Reader, Reports Reader, Application Administrator ou Privileged Role Administrator, conforme o recurso). Consultar OAuth2PermissionGrant é um exemplo, com permissões delegadas, é preciso ter o escopo e um papel administrativo autorizado, sem isso, o cmdlet retorna Forbidden mesmo com o Connect-MgGraph -Scopes correto, em acesso app-only, não há usuário: vale somente a permissão de aplicação consentida pelo administrador, antes de padronizar um runbook, confirme na documentação de cada API quais roles são aceitas.

O ponto que vale destacar: funcionar com uma permissão ampla não significa que essa seja a permissão correta, é comum ver scripts conectando com Directory.ReadWrite.All ou RoleManagement.ReadWrite.Directory para tarefas puramente de leitura, funciona, mas concede uma capacidade de escrita que a tarefa nunca usa, e que passa a existir como superfície de risco enquanto a sessão ou a aplicação estiver ativa.

Automação: interativo não é produção

Todos os exemplos deste artigo assumem uma sessão iniciada com Connect-MgGraph em modo delegado interativo, excelente para exploração, troubleshooting, administração pontual e para a própria investigação que construímos aqui.

Automações recorrentes seguem um caminho diferente, em vez de um usuário autenticando interativamente a cada execução, a automação normalmente usa autenticação app-only, baseada em uma identidade de aplicação, vale separar dois caminhos que costumam ser misturados, mas que são conceitualmente distintos:

App Registration
     ↓
Service Principal
     ↓
Certificate / Federated Identity Credential
     ↓
Application Permissions
     ↓
Microsoft Graph
Azure Resource
     ↓
Managed Identity
     ↓
Service Principal
     ↓
Permissions
     ↓
Microsoft Graph

A diferença não é só de origem, é estrutural, um Service Principal de Managed Identity não possui um Application Object associado, ele é provisionado diretamente a partir do recurso Azure (uma Function, uma VM, um runbook de Automation) e não tem, do outro lado, um registro de aplicação com passwordCredentials, keyCredentials ou federatedIdentityCredentials para gerenciar, managed Identity, portanto, não é uma credencial de uma App Registratio, é um caminho de autenticação inteiramente separado, em que a plataforma Azure cuida da credencial e a workload não precisa armazenar nem rotacionar segredo algum.

Duas escolhas de design importam aqui, sem que seja necessário um tutorial completo de configuração:

  • Client Secret não deveria ser a primeira opção para automações sensíveis com App Registration, é uma string que precisa ser armazenada, protegida e rotacionada manualmente.
  • Entre as alternativas, os níveis de esforço são diferentes. Federated Identity Credentials são preferidas quando a workload roda em um ambiente que consegue emitir um token confiável (por exemplo, GitHub Actions ou Kubernetes), pois dispensam credencial persistente, certificados são uma melhoria sobre secrets, mas continuam exigindo proteção da chave privada, controle de acesso e renovação, e Managed Identity só faz sentido quando a workload roda em um ambiente Azure que a suporte; fora desses cenários, a escolha realista permanece entre Federated Identity Credential, certificado e secret, dentro do caminho de App Registration.

Quando uma investigação começa a ficar difícil

Considere uma pergunta aparentemente simples: “quero descobrir quais aplicações possuem determinadas permissões e quem são seus owners”. Para responder, é preciso transitar por Application, Service Principal, OAuth2PermissionGrant, AppRoleAssignment e Owner e, antes disso, descobrir qual módulo, cmdlet, endpoint, versão de API e permissão cada etapa exige.

Outra pergunta: “quais usuários podem obter Global Administrator?”, a resposta não é apenas listar quem tem a role atribuída agora, isso ignora quem está apenas elegível via PIM, a resposta correta soma Directory Roles, PIM eligibility, PIM active assignments e, como vimos, privilégios herdados por grupos e PIM for Groups.

Pergunta de negócio
        ↓
Objeto Microsoft Graph
        ↓
Endpoint
        ↓
Cmdlet
        ↓
Módulo
        ↓
Permissão (e role do operador)

Esse processo de tradução de uma pergunta de negócio até o cmdlet e a permissão exatos passa a fazer parte do trabalho tanto quanto a própria investigação, e à medida que a superfície do Graph continua crescendo, essa etapa de descoberta tende a consumir tempo proporcionalmente maior do que a investigação em si.

Conclusão

O primeiro artigo desta série respondeu “o que existe no Microsoft Graph para identidade?”. Este segundo artigo tentou responder a uma pergunta diferente: “como relacionar esses dados para investigar identidade?”

O Graph permite sair de inventários isolados (uma lista de usuários, uma lista de aplicações) para uma visão conectada: usuários, grupos, privilégios, PIM, aplicações, Service Principals, credenciais, permissões, consentimento, risco e atividade, todos acessíveis com autenticação unificada e um modelo de permissões granular o suficiente para aplicar Least Privilege em cada etapa.

Quanto mais avançamos nessas investigações, porém, surge um novo desafio: não necessariamente obter os dados, mas descobrir rapidamente qual objeto, endpoint, cmdlet, módulo e permissão levam até eles.

Esse problema de descoberta será o ponto de partida do próximo artigo da série, onde apresentarei uma abordagem que venho construindo para tornar essa navegação mais simples.

Nota: sobre Get-MgApplicationFederatedIdentityCredential: a permissão indicada neste artigo (Application.Read.All) refere-se ao endpoint de listagem (GET /applications/{id}/federatedIdentityCredentials). Confirme na documentação oficial o comportamento de cada endpoint individual antes de usar o cmdlet em automações.

Referências oficiais

Chegamos ao final deste artigo!

O que você achou dessa abordagem de investigar identidade correlacionando sinais no Microsoft Graph?
Na sua organização, a investigação de usuários e aplicações é feita com scripts próprios, ferramentas prontas ou só quando um alerta dispara?

Compartilhe sua experiência com a comunidade e deixe sua opinião nos comentários.

Você também pode gostar

Deixe um comentário

Este site utiliza o Akismet para reduzir spam. Saiba como seus dados em comentários são processados.

erro: Este conteúdo está protegido.

Olá! Este site utiliza cookies para melhorar sua experiência de navegação, personalizar conteúdo e anúncios, fornecer funcionalidades de redes sociais e analisar nosso tráfego. Ao continuar a usar nosso site, você concorda com o uso de cookies. Aceitar