Registro em log de auditoria para princípios do Microsoft Entra ID no servidor flexível do Banco de Dados do Azure para PostgreSQL

As auditorias de banco de dados são um componente importante dos requisitos de conformidade da sua organização. Monitorando atividades direcionadas, você pode obter sua linha de base de segurança. No servidor flexível do Banco de Dados do Azure para PostgreSQL, você pode configurar auditorias usando a extensão pg pgaudit, conforme descrito no log de auditoria no Banco de Dados do Azure para PostgreSQL.

Um desafio é usar o recurso de auditoria ao lado da autenticação da ID do Microsoft Entra quando você estiver usando grupos de ID do Microsoft Entra e quiser auditar as ações de membros individuais do grupo. Esse desafio existe porque os membros do grupo se conectam usando seus tokens de acesso pessoal, mas usam o nome de grupo como nome de usuário.

A KQL (Linguagem de Consulta Kusto) é uma linguagem de consulta baseada em pipeline e somente leitura que permite consultar logs de serviço do Azure. O KQL dá suporte à consulta de logs do Azure para analisar rapidamente um alto volume de dados. Para este artigo, use KQL para consultar os logs do Azure PostgreSQL e extrair informações do usuário do Microsoft Entra ID dos logs de auditoria.

Pré-requisitos

  1. Habilitar log de auditoria – Log de auditoria no Banco de Dados do Azure para PostgreSQL
  2. Habilitar o envio dos logs do Azure Postgres para o Azure Log Analytics - Configurar e acessar logs
  3. Ajuste o parâmetro log_line_prefix: no painel Parâmetros, defina log_line_prefix para incluir as sequências de escape user=%u,db=%d,session=%c,sess_time=%s na mesma sequência para obter os resultados desejados.
    • Antes: log_line_prefix = %t-%c-
    • Depois: log_line_prefix = %t-%c-user=%u,db=%d,session=%c,sess_time=%s

Consulta Kusto

A consulta Kusto a seguir consulta as AzureDiagnostics duas vezes.
A primeira subconsulta localiza todas as linhas que contêm a cadeia de caracteres Microsoft Entra ID connection authorized e extrai o PrincipalName destas linhas de log, junto com o SessionId.
A segunda subconsulta localiza todos os logs de auditoria.
Por fim, essas duas subconsultas são unidas no SessionId.

let lookbackTime = ago(3d);
let opindex = 3;
let startIndex = toscalar(range thirdIndex from opindex to opindex step 1
    | project thirdIndex);
AzureDiagnostics
| where ResourceProvider == 'MICROSOFT.DBFORPOSTGRESQL'
| where TimeGenerated >= lookbackTime
| where Message contains 'Microsoft Entra ID connection authorized'
| extend SessionId = tostring(split(tostring(split(Message, 'session=')[-1]), ',sess_time')[-2])
| extend UPN = iff(Message contains 'UPN',tostring(split(tostring(split(Message, 'UPN=')[-1]), 'oid=')[-2]), '')
| extend appId = iff(Message contains 'appid', tostring(split(tostring(split(Message, 'appid=')[-1]), 'oid=')[-2]), '')
| extend PrincipalName = strcat(UPN, appId)
| project SessionId, PrincipalName
| join kind=leftouter
    (
    AzureDiagnostics
    | where ResourceProvider == 'MICROSOFT.DBFORPOSTGRESQL'
    | where TimeGenerated >= lookbackTime
    | where Message contains 'AUDIT: SESSION'
    | extend RoleName = tostring(split(tostring(split(Message, 'user=')[-1]), ',db')[-2])
    | where RoleName !in ('azuresu', '[unknown]', 'postgres', '')
    | extend SessionId = tostring(split(tostring(split(Message, 'session=')[-1]), ',sess_time')[-2])
    | extend SubMessage = tostring(split(Message, 'SESSION,')[-1])
    | extend splitArray = split(SubMessage, ',')
    | extend SqlQueryP1 = tostring(split(tostring(split(Message, ',,,')[-1]), ',<')[-2])
    | extend SqlQueryP2 = replace_string(tostring(split(SqlQueryP1, ',\"')[-1]), '"', '')
    | extend SqlQueryP3 = tostring(split(Message, ',,,')[1])
    | extend OperationType = tostring(splitArray[startIndex])
    | extend SqlQuery = trim('"', case(OperationType == 'EXECUTE', SqlQueryP2, SqlQueryP1 == '', SqlQueryP3, SqlQueryP1))
    )
    on $left.SessionId == $right.SessionId
| project TimeGenerated, PrincipalName, RoleName, OperationType, SqlQuery

Resultados de exemplo

A tabela resultante tem esta aparência:

TimeGenerated Nome principal Nome da Função TipoDeOperação SqlQuery
2025-12-12T16:25:05.104Z user@example.com NomeDoGrupoExemplo SELECT select * from pg_seclabels;
2025-12-12T16:25:04.000Z user@example.com user@example.com SELECT select * from pg_seclabels;

Se o usuário entrar como uma função de grupo, as colunas PrincipalName e RoleName mostrarão valores diferentes, como na primeira linha do exemplo.
O PrincipalName valor identifica o usuário que entrou. O RoleName valor identifica a função no PostgreSQL que o usuário acessa após entrar.

PrincipalName é o UPN (Nome da Entidade de Usuário) ou o AppId, dependendo se a entidade de usuário ou a entidade de serviço for conectada.