Rejestrowanie inspekcji dla podmiotów zabezpieczeń Microsoft Entra ID na serwerze elastycznym Azure Database for PostgreSQL

Inspekcje bazy danych są ważnym składnikiem wymagań dotyczących zgodności organizacji. Monitorując działania docelowe, można osiągnąć punkt odniesienia zabezpieczeń. Na elastycznym serwerze usługi Azure Database for PostgreSQL można skonfigurować audyty przy użyciu rozszerzenia pgaudit, zgodnie z opisem w temacie Rejestrowanie audytów w usłudze Azure Database for PostgreSQL.

Jednym z wyzwań jest użycie funkcji inspekcji wraz z uwierzytelnianiem identyfikatora Entra firmy Microsoft, gdy używasz grup identyfikatorów Entra firmy Microsoft i chcesz przeprowadzić inspekcję akcji poszczególnych członków grupy. To wyzwanie istnieje, ponieważ członkowie grupy logują się przy użyciu osobistych tokenów dostępu, ale używają nazwy grupy jako nazwy użytkownika.

Kusto Query Language (KQL) to zaawansowany język zapytań oparty na potokach, który umożliwia wykonywanie zapytań dotyczących dzienników usługi platformy Azure. Język KQL obsługuje wykonywanie zapytań dotyczących dzienników platformy Azure w celu szybkiego analizowania dużej ilości danych. W tym artykule użyj języka KQL, aby wysyłać zapytania do dzienników usługi Azure Postgres i wyodrębniać informacje o użytkownikach identyfikatora Entra firmy Microsoft z dzienników inspekcji.

Wymagania wstępne

  1. Włączanie rejestrowania inspekcji — rejestrowanie inspekcji w usłudze Azure Database for PostgreSQL
  2. Włączanie wysyłania dzienników Azure Postgres do Azure Log Analytics — konfigurowanie dzienników i uzyskiwanie do ich dostępu
  3. Dostosuj parametr log_line_prefix: w sekcji Parametry ustaw log_line_prefix tak, aby zawierał sekwencje ucieczki user=%u,db=%d,session=%c,sess_time=%s w tej samej kolejności, aby uzyskać oczekiwane wyniki.
    • Przed: log_line_prefix = %t-%c-
    • Po: log_line_prefix = %t-%c-user=%u,db=%d,session=%c,sess_time=%s

Zapytanie Kusto

Następujące zapytanie Kusto zapytuje AzureDiagnostics dwa razy.
Pierwsze podzapytanie znajduje wszystkie wiersze zawierające ciąg Microsoft Entra ID connection authorized i wyodrębnia PrincipalName z tych wierszy dziennika, obok elementu SessionId.
Drugie podzapytanie znajduje wszystkie logi audytu.
Na koniec te dwie podzapytania są przyłączone do elementu 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

Przykładowe wyniki

Wynikowa tabela wygląda następująco:

Godzina utworzenia NazwaGłówna Nazwa roli Typ operacji Zapytanie sql
2025-12-12T16:25:05.104Z user@example.com PrzykładowaNazwaGrupy SELECT wybierz * z pg_seclabels;
2025-12-12T16:25:04.000Z user@example.com user@example.com SELECT wybierz * z pg_seclabels;

Jeśli użytkownik loguje się jako rola w grupie, kolumny PrincipalName i RoleName pokazują różne wartości, jak w pierwszym wierszu przykładu.
Wartość PrincipalName identyfikuje użytkownika, który się zalogował. Wartość RoleName identyfikuje rolę w usłudze PostgreSQL, do której użytkownik uzyskuje dostęp po zalogowaniu.

PrincipalName jest główną nazwą użytkownika (UPN) lub identyfikatorem aplikacji (AppId), w zależności od tego, czy loguje się główny użytkownik czy główna usługa.