Registrazione dei log di audit nel server flessibile di Database di Azure per PostgreSQL per le entità servizio di Microsoft Entra ID

I controlli del database sono un componente importante dei requisiti di conformità dell'organizzazione. Monitorando le attività mirate, è possibile ottenere la baseline di sicurezza. Nel servizio flessibile di Azure Database per PostgreSQL, è possibile configurare la registrazione degli audit usando l'estensione pgaudit, come descritto in Audit logging in Database di Azure per PostgreSQL.

Una sfida consiste nell'usare la funzionalità di controllo insieme all'autenticazione microsoft Entra ID quando si usano i gruppi di ID Microsoft Entra e si vogliono controllare le azioni dei singoli membri del gruppo. Questo problema si verifica perché i membri del gruppo accedono tramite i loro token di accesso personali, ma usano il nome del gruppo come nome utente.

Il linguaggio di query Kusto (KQL) è un potente linguaggio di query basato su pipeline e di sola lettura che consente di eseguire query sui log del servizio di Azure. KQL supporta l'esecuzione di query sui log di Azure per analizzare rapidamente un volume elevato di dati. Per questo articolo, usare KQL per eseguire query sui log di Azure Postgres ed estrarre le informazioni utente dell'ID Entra di Microsoft dai log di controllo.

Prerequisiti

  1. Abilitare la registrazione di controllo - Registrazione di controllo in Database di Azure per PostgreSQL
  2. Abilitare l'invio dei log di Azure Postgres ad Log Analytics di Azure - Configurare e accedere ai log
  3. Modificare il parametro log_line_prefix: dal pannello Parametri, impostare log_line_prefix in modo da includere i caratteri di escape user=%u,db=%d,session=%c,sess_time=%s nella stessa sequenza per ottenere i risultati desiderati.
    • Prima: log_line_prefix = %t-%c-
    • Dopo: log_line_prefix = %t-%c-user=%u,db=%d,session=%c,sess_time=%s

Query Kusto

La query seguente in Kusto esegue due volte una query su AzureDiagnostics.
La prima sottoquery trova tutte le righe che contengono la stringa Microsoft Entra ID connection authorized ed estrae il PrincipalName da queste righe di log, insieme a SessionId.
La seconda sottoquery trova tutti i log di controllo.
Infine, queste due sottoquery vengono unite tramite il join su 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

Risultati di esempio

La tabella risultante è simile alla seguente:

TimeGenerated Nome principale NomeRuolo Tipo di Operazione SqlQuery
2025-12-12T16:25:05.104Z user@example.com NomeGruppoEsempio SELECT selezionare * da pg_seclabels;
2025-12-12T16:25:04.000Z user@example.com user@example.com SELECT selezionare * da pg_seclabels;

Se l'utente accede come ruolo di gruppo, le PrincipalName colonne e RoleName mostrano valori diversi, ad esempio nella prima riga dell'esempio.
Il PrincipalName valore identifica l'utente che ha eseguito l'accesso. Il RoleName valore identifica il ruolo in PostgreSQL a cui l'utente accede dopo l'accesso.

PrincipalName è il Nome dell'entità utente (UPN) o AppId a seconda che l'entità utente o l'entità servizio siano state registrate.