☁️Entra IDApplicationsPermissionsIdentity

Entra Acesso Delegado versus Acesso de Aplicativo na Graph API: O Modelo de Consentimento Que a Maioria dos Tenants Entende ao Contrário

Permissões delegadas do Graph não são as seguras. Uma concessão AllPrincipals válida para todo o tenant pré-autoriza um app contra todos os usuários do diretório — e vive em uma coleção que revisões de app roles nunca leem.

Younes AZABARPor Younes AZABAR14 min de leitura
Entra Acesso Delegado versus Acesso de Aplicativo na Graph API: O Modelo de Consentimento Que a Maioria dos Tenants Entende ao Contrário

Todo tenant precisa, mais cedo ou mais tarde, responder à pergunta de acesso delegado versus acesso de aplicativo na Graph API do Entra: qual dos dois modelos de consentimento é realmente mais seguro, e como conceder a um app o mínimo necessário sem quebrá-lo? A maioria das equipes responde por instinto — permissões delegadas "rodam como um usuário", então parecem limitadas; permissões de aplicativo "rodam como o app", então parecem perigosas. Esse instinto está certo pela metade, e a metade errada é justamente aquela que ninguém audita.

A permissão que discretamente dá a um app um ponto de apoio em cada caixa de correio do tenant não é necessariamente uma permissão de aplicativo. Pode ser uma permissão delegada à qual um administrador consentiu em nome de toda a organização — e essa concessão vive em uma coleção de objetos diferente das app roles, de modo que uma revisão de permissões de aplicativo nunca a retorna.

Entra Acesso Delegado versus Acesso de Aplicativo na Graph API, Definido

A plataforma de identidade da Microsoft documenta dois cenários de acesso, e a distinção está em como quem o app age — não em quanto dano ele pode causar.

Acesso delegado significa que um usuário fez login e o aplicativo cliente acessa o recurso em nome dele. A Microsoft é explícita quanto ao teto: "O aplicativo não é capaz de acessar nada que o usuário conectado não pudesse acessar." Um app com a permissão delegada Files.Read.All "só é capaz de ler arquivos que o usuário pode acessar pessoalmente." Permissões delegadas também são chamadas de scopes, e o objeto de concessão resultante é um oAuth2PermissionGrant.

Acesso de aplicativo (app-only) significa que o aplicativo age por conta própria, sem nenhum usuário conectado — daemons, backups, automação. Aqui o teto é o próprio recurso: um app com a permissão de aplicativo do Microsoft Graph Files.Read.All "é capaz de ler qualquer arquivo no tenant usando o Microsoft Graph." Permissões de aplicativo são app roles, o objeto de concessão é um appRoleAssignment, e, de acordo com a própria tabela comparativa da Microsoft, apenas um administrador pode consentir com elas.

Permissões delegadasPermissões de aplicativo
Contexto de acessoEm nome de um usuário conectadoSem usuário
Quem pode consentirUsuários para os próprios dados; admins para todos os usuáriosApenas um administrador
Métodos de consentimentoEstático (registro de aplicativo) ou dinâmico (solicitado no login)Apenas estático
Outros nomesScopes, OAuth2 permission scopesApp roles, permissões app-only
Objeto de concessãooAuth2PermissionGrantappRoleAssignment

Essa última linha é o problema inteiro em uma frase. Os dois tipos de permissão vivem em duas coleções de objetos diferentes. Uma auditoria que enumera appRoleAssignments não vê nada do lado delegado, e vice-versa: as duas consultas retornam conjuntos disjuntos, de modo que uma revisão que executa apenas uma delas fica silenciosa sobre metade da superfície de ataque.

As Duas Formas Como os Tenants Leem o Modelo de Consentimento ao Contrário

"O delegado é o seguro"

A frase "o app não pode fazer mais do que o usuário pode" é verdadeira — e ela está carregando um peso que nunca deveria ter carregado.

Uma concessão delegada carrega um campo consentType com dois valores possíveis. A referência da Microsoft é inequívoca: "AllPrincipals indica autorização para representar todos os usuários. Principal indica autorização para representar um usuário específico." Quando consentType é AllPrincipals, principalId é nulo — a concessão não está vinculada a ninguém, porque se aplica a todos.

Ou seja, um admin consent válido para todo o tenant para a permissão delegada Mail.ReadWrite não significa que o app pode ler todos os e-mails. Significa que o app está pré-autorizado a agir na caixa de correio de qualquer usuário que faça login nele, sem prompt de consentimento, enquanto a concessão existir. O limite continua sendo os privilégios do próprio usuário conectado — mas a população é todo o diretório, e o atrito que normalmente faria um usuário parar e ler uma tela de consentimento desaparece.

Agora pense em quem faz login em ferramentas internas. Se um Administrador Global ou um Exchange Administrator se autentica em um app que detém uma permissão delegada Directory.ReadWrite.All válida para todo o tenant, o app herda o alcance dessa sessão. Delegado não é um teto fixo de privilégio; é um teto que se move junto com quem quer que esteja logado.

"Permissões de aplicativo são as poderosas, então peça essas"

O erro espelhado. O próprio passo a passo do portal da Microsoft apresenta permissões delegadas primeiro e descreve permissões de aplicativo como a opção para "aplicativos do tipo serviço ou daemon que precisam acessar uma web API como eles mesmos, sem interação do usuário para login ou consentimento" — e é exatamente essa descrição que faz os desenvolvedores recorrerem a elas. Um serviço que só precisa ler uma única caixa de correio compartilhada acaba detendo Mail.Read contra toda a organização, porque essa é a única granularidade que o Entra ID oferece na própria permissão do Graph.

A introdução da Microsoft sobre acesso delegado é direta quanto a qual cenário é qual: acesso delegado costuma ser "uma escolha ruim para cenários que precisam rodar sem um usuário conectado, como automação", enquanto você deve "usar acesso delegado sempre que quiser permitir que um usuário conectado trabalhe com os próprios recursos ou recursos aos quais tem acesso." O modo de falha em tenants reais é escolher pela conveniência — o que quer que autentique sem prompt de navegador — em vez de pela pergunta de qual principal realmente precisa dos dados.

⚠️

⚠️ Aviso: Os dois erros se somam. Um tenant que concede em excesso tanto scopes delegados válidos para todo o tenant quanto permissões de aplicativo sem escopo tem dois caminhos independentes e aditivos para os mesmos dados, rastreados em dois lugares diferentes.

O Que uma Concessão Delegada Válida para Todo o Tenant Dá a um Atacante

O acesso delegado exige duas autorizações independentes para ter sucesso, e a Microsoft as documenta separadamente: autorização do app cliente (o app recebeu o scope, que cai na claim scp do token) e autorização do usuário (o recurso verifica os próprios direitos do usuário conectado). O exemplo do OneDrive da Microsoft deixa a matriz explícita — uma requisição falha se ou o scope estiver ausente ou o usuário não tiver direitos sobre o objeto.

Um atacante não precisa quebrar esse modelo. Ele precisa satisfazer as duas metades ao mesmo tempo, e uma concessão válida para todo o tenant entrega a primeira metade de graça:

  1. O scope já está concedido. Com consentType = AllPrincipals, não há prompt de consentimento para phishing e nenhuma concessão por usuário a ser criada. Qualquer token que o app obtenha para qualquer usuário carrega o scope.
  2. A metade do usuário chega junto com a vítima. O que quer que o usuário comprometido ou vítima de phishing consiga alcançar, o app agora consegue alcançar via API — exatamente a diferença entre "ler essa única caixa de entrada no Outlook" e "enumerá-la programaticamente na velocidade de uma máquina." E esse alcance é frequentemente maior do que o organograma sugere — veja funções de admin de service principal com privilégios excessivos.
  3. A revogação não fica onde você espera. A própria orientação da Microsoft observa que, no Entra admin center, "você não pode revogar permissões na aba User consent usando o portal." Concessões delegadas por usuário precisam ser removidas via Graph ou PowerShell.

Essa é a mesma superfície de consentimento abusada pelo OAuth consent phishing, só que aqui nenhum phishing é necessário — a concessão já está em vigor, feita por um administrador, geralmente anos atrás, geralmente para uma ferramenta que ainda está instalada.

Duas propriedades de registro relacionadas ampliam ainda mais o raio de impacto. signInAudience definido como AzureADMultipleOrgs torna o registro multi-tenant — "usuários com uma conta corporativa ou de estudante da Microsoft em qualquer tenant do Microsoft Entra de qualquer organização" podem fazer login. E apps que ninguém mais usa mantêm suas concessões indefinidamente: o objeto oAuth2PermissionGrant não carrega nenhuma propriedade de expiração, então nada faz uma concessão envelhecer por conta própria, e uma integração desativada com um secret ainda válido e uma concessão válida para todo o tenant é uma credencial esperando para ser encontrada. Veja também rotação de credenciais de registro de aplicativo Entra.

Detecção

Execute estas consultas contra o próprio tenant. Ler a atividade de login exige AuditLog.Read.All (o mínimo privilegiado) e uma das funções Reports Reader, Security Reader ou Security Administrator; ler e revogar concessões exige pelo menos Cloud Application Administrator.

O que procurarOndeSinal
Concessões delegadas válidas para todo o tenantoauth2PermissionGrantsconsentType eq 'AllPrincipals' — aplica-se a todo usuário
Concessões delegadas por usuáriooauth2PermissionGrantsconsentType eq 'Principal', não revogável no portal
Permissões de aplicativoservicePrincipals/{id}/appRoleAssignmentsSem escopo, válida para todo o tenant por construção
Registros multi-tenantapplicationssignInAudience eq 'AzureADMultipleOrgs'
Apps inativos/beta/reports/servicePrincipalSignInActivitieslastSignInActivity com mais de 90 dias

Comece pelas concessões que se aplicam a todos. consentType suporta $filter com eq:

GET https://graph.microsoft.com/v1.0/oauth2PermissionGrants?$filter=consentType eq 'AllPrincipals'

Cada resultado fornece clientId (o object id do service principal cliente — não seu appId), resourceId (a API sendo chamada) e scope, uma lista separada por espaços como openid User.Read GroupMember.Read.All. O equivalente no Microsoft Graph PowerShell:

Connect-MgGraph -Scopes "Directory.Read.All"
Get-MgOauth2PermissionGrant -All | Where-Object { $_.ConsentType -eq 'AllPrincipals' } |
    Select-Object ClientId, ResourceId, Scope

Em seguida, busque a outra coleção para o mesmo service principal, porque a consulta delegada acima nunca vai mostrá-la:

GET https://graph.microsoft.com/v1.0/servicePrincipals/{id}/oauth2PermissionGrants
GET https://graph.microsoft.com/v1.0/servicePrincipals/{id}/appRoleAssignments

Registros multi-tenant — signInAudience suporta $filter com eq, ne e not:

GET https://graph.microsoft.com/v1.0/applications?$filter=signInAudience eq 'AzureADMultipleOrgs'

Para apps inativos, a atividade de login de service principals é uma API beta — a Microsoft afirma que APIs sob /beta estão sujeitas a mudanças e não são suportadas em produção, então trate isso como um insumo de auditoria, não como um controle sobre o qual construir. Ela suporta $top, $filter e $orderby:

GET https://graph.microsoft.com/beta/reports/servicePrincipalSignInActivities
Import-Module Microsoft.Graph.Beta.Reports
Get-MgBetaReportServicePrincipalSignInActivity

Cada registro expõe lastSignInActivity mais uma quebra que mostra como o app está sendo usado: delegatedClientSignInActivity (ele está agindo por um usuário conectado) versus applicationAuthenticationClientSignInActivity (app-only, sem usuário). Essa divisão é o jeito mais rápido de flagrar um app que detém os dois tipos de permissão mas só exercita um deles na prática, e ela alimenta diretamente a detecção de anomalias de login de service principals.

Remediação

💡

💡 Vitória Rápida: Enumere consentType eq 'AllPrincipals' primeiro e revogue as concessões vinculadas a apps para os quais ninguém consegue nomear um owner. É o caminho mais curto entre a auditoria e a redução do raio de impacto.

  1. Barre o excesso de consentimento novo na porta. No Microsoft Entra admin center, vá em Identity > Applications > Enterprise apps > Consent and permissions > User consent settings (Administrador Global para o portal; Privileged Role Administrator caso contrário). Por padrão, "todos os usuários têm permissão para consentir com aplicativos para permissões que não exigem consentimento de administrador." As duas políticas integradas são microsoft-user-default-legacy (permitir consentimento de usuário para apps — qualquer permissão que não exija admin consent, qualquer app) e microsoft-user-default-low (permitir consentimento de usuário apenas para apps de editores verificados ou registrados no próprio tenant, e apenas para permissões classificadas como baixo impacto). Programaticamente:
PATCH https://graph.microsoft.com/v1.0/policies/authorizationPolicy

{
    "defaultUserRolePermissions": {
        "permissionGrantPoliciesAssigned": [
            "managePermissionGrantsForSelf.microsoft-user-default-low",
            "managePermissionGrantsForOwnedResource.{other-current-policies}"
        ]
    }
}

Leia primeiro a authorizationPolicy atual e preserve quaisquer entradas ManagePermissionGrantsForOwnedResource.* existentes, ou você removerá silenciosamente o self-service consent dos desenvolvedores. Combine isso com o admin consent workflow para que usuários possam solicitar uma revisão em vez de ficarem sem saída, e revise isso junto com as demais configurações padrão de usuário do tenant que regem o que não-administradores podem criar.

⚠️

⚠️ Aviso: Alterar essa configuração corrige apenas o futuro. A Microsoft é explícita: "Quaisquer atualizações nas configurações de consentimento do usuário afetam apenas operações de consentimento futuras para aplicativos. Concessões de consentimento existentes permanecem inalteradas." Apertar a política não revoga uma única concessão existente.

  1. Revogue o que já existe. Permissões delegadas e de aplicativo são removidas por endpoints diferentes:
DELETE https://graph.microsoft.com/v1.0/oAuth2PermissionGrants/{id}
DELETE https://graph.microsoft.com/v1.0/servicePrincipals/{resource-servicePrincipal-id}/appRoleAssignedTo/{appRoleAssignment-id}

Lembre-se da ressalva do portal: a aba Admin consent suporta revogação pela UI, a aba User consent não. E revogar sozinho não é duradouro — a Microsoft observa que "revogar a permissão atualmente concedida não impede que os usuários consintam novamente com as permissões solicitadas pelo aplicativo." Você também precisa remover a permissão daquilo que o app solicita, ou bloquear o consentimento de usuário por completo.

  1. Delimite o escopo das permissões de aplicativo em vez de aceitar acesso válido para todo o tenant. Para workloads do Exchange, o RBAC for Applications permite combinar uma app role com um resource scope, de modo que Mail.Read cubra um conjunto filtrado de caixas de correio em vez de todas elas. Atribuir essas funções exige ser membro do grupo de função Organization Management no Exchange Online, além da função Exchange Administrator no Microsoft Entra ID:
New-ServicePrincipal -AppId <appId> -ObjectId <servicePrincipalObjectId> -DisplayName "reporting-app"
New-ManagementScope -Name "Canadian users" -RecipientRestrictionFilter "CustomAttribute1 -eq '012332'"
New-ManagementRoleAssignment -App <servicePrincipalObjectId> -Role "Application Mail.Read" -CustomResourceScope "Canadian users"
Test-ServicePrincipalAuthorization -Identity "reporting-app" -Resource <targetMailbox> | Format-Table

Há uma armadilha aqui que pega quase todo mundo. O FAQ da Microsoft deixa claro: as permissões das duas autoridades formam uma união, não uma interseção. "Se o seu Service Principal tem Mail.Read concedido no Microsoft Entra ID e você configura uma permissão Mail.Read com escopo de recurso no Application RBAC, é importante remover a atribuição de Mail.Read do Microsoft Entra ID. Caso contrário, a união ... resulta em nenhum escopo de recurso efetivo." Delimitar o escopo no Exchange enquanto a concessão no Entra permanece não muda nada. Observe também que mudanças de permissão ficam em cache entre 30 minutos e 2 horas (o cmdlet de teste ignora o cache), e que resource scopes exclusivos não restringem o acesso do app.

  1. Aposente registros inativos. Qualquer coisa sem lastSignInActivity há 90 dias é candidata a desativar-e-depois-excluir. Siga essa ordem para poder restaurar rapidamente caso se descubra que um job trimestral depende dela. Excluir o service principal no Entra também remove automaticamente suas atribuições de RBAC do Exchange.

  2. Torne a revisão recorrente, e cubra as duas coleções. Uma revisão que só lê appRoleAssignments vai aprovar um tenant cuja exposição real é uma concessão AllPrincipals de cinco anos atrás. Incorpore isso à auditoria de segurança do Entra ID mais ampla, em vez de tratar como uma limpeza pontual — concessões de consentimento não têm expiração, então esse desvio permanece indefinidamente até que alguém olhe.

Como o EtcSec Detecta Isso

O catálogo Azure do EtcSec cobre as duas metades do modelo de consentimento. APP_EXCESSIVE_DELEGATED sinaliza aplicativos que detêm mais scopes delegados do que sua função justifica, e APP_CONSENT_GRANTED_TENANT_WIDE revela as concessões AllPrincipals que uma revisão de permissões baseada em app roles deixaria passar. APP_USER_CONSENT_UNRESTRICTED e APP_ADMIN_CONSENT_NOT_REQUIRED verificam as configurações em nível de tenant que permitem que essas concessões se acumulem em primeiro lugar, enquanto APP_MULTI_TENANT identifica registros com signInAudience definido além do próprio diretório, e APP_UNUSED_90_DAYS lista os registros que ainda detêm concessões ativas muito tempo depois que alguém parou de usá-los.

Para o lado das permissões de aplicativo do mesmo problema — especificamente as app roles Directory, Mail, Files e RoleManagement — veja permissões perigosas do Graph API em registros de aplicativo Entra e registros de aplicativo Azure com privilégios excessivos.

ℹ️

ℹ️ Nota: O EtcSec verifica essas configurações incorretas em toda auditoria do Entra ID. Execute uma auditoria gratuita para ver quais dos seus registros de aplicativo detêm concessões válidas para todo o tenant hoje.

Fontes

Explore as paginas de identidade que sustentam este tema