Registo de auditoria para identidades do Microsoft Entra ID no Base de Dados do Azure para PostgreSQL flexible server

As auditorias de bases de dados são um componente importante dos requisitos de conformidade da sua organização. Ao monitorizar as atividades direcionadas, pode atingir a sua linha de base de segurança. No Base de Dados do Azure para PostgreSQL flexible server, pode configurar auditorias usando a extensão pgaudit PG, conforme descrito em Audit logging no Base de Dados do Azure para PostgreSQL.

Um dos desafios é usar a funcionalidade de auditoria juntamente com a autenticação do Microsoft Entra ID quando se utilizam grupos Microsoft Entra ID e se pretende auditar as ações dos membros individuais do grupo. Este desafio existe porque os membros do grupo iniciam sessão usando os seus tokens de acesso pessoais, mas usam o nome do grupo como nome de utilizador.

Kusto Query Language (KQL) é uma poderosa linguagem de consulta apenas de leitura, baseada em pipelines, que permite consultar os registos de serviço do Azure. O KQL suporta consultar logs do Azure para analisar rapidamente um grande volume de dados. Para este artigo, use o KQL para consultar os registos do Azure Postgres e extrair informações de utilizador do Microsoft Entra ID a partir dos registos de auditoria.

Pré-requisitos

  1. Ativar registo de auditoria - Registo de auditoria na Azure Database para PostgreSQL
  2. Permitir o envio dos logs do Azure Postgres para o Azure Log Analytics - Configurar e aceder aos logs
  3. Ajuste o parâmetro log_line_prefix: No painel Parâmetros, defina o log_line_prefix de modo a incluir os caracteres de escape user=%u,db=%d,session=%c,sess_time=%s na mesma sequência para obter os resultados pretendidos.
    • Antes: log_line_prefix = %t-%c-
    • Depois: log_line_prefix = %t-%c-user=%u,db=%d,session=%c,sess_time=%s

Consulta Kusto

A seguinte consulta Kusto consulta o AzureDiagnostics duas vezes.
A primeira subconsulta encontra todas as linhas que contêm a cadeia Microsoft Entra ID connection authorized e extrai o PrincipalName dessas linhas logarítmicas, juntamente com o SessionId.
A segunda subconsulta encontra todos os registos de auditoria.
Finalmente, estas 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 é a seguinte:

TimeGenerated Nome Principal Nome da Função TipoDeOperação SqlQuery
2025-12-12T16:25:05.104Z user@example.com ExemploNome do Grupo SELECT selecionar * de pg_seclabels;
2025-12-12T16:25:04.000Z user@example.com user@example.com SELECT selecionar * de pg_seclabels;

Se o utilizador iniciar sessão como um papel de grupo, as PrincipalName colunas e RoleName mostram valores diferentes, como na primeira linha do exemplo.
O PrincipalName valor identifica o utilizador que iniciou sessão. O RoleName valor identifica o papel no PostgreSQL ao qual o utilizador acede após iniciar sessão.

PrincipalName é o Nome do Principal do Utilizador (UPN) ou o AppId, dependendo se o principal de utilizador ou o principal de serviço inicia sessão.