資料庫稽核是貴組織合規要求的重要組成部分。 透過監控目標活動,您可以達成您的安全基準。 在 適用於 PostgreSQL 的 Azure 資料庫 彈性伺服器中,你可以使用 pgaudit PG 擴充功能設定稽核,如 適用於 PostgreSQL 的 Azure 資料庫 的稽核日誌所述。
其中一個挑戰是,當你使用 Microsoft Entra ID 群組並想稽核個別群組成員的行為時,如何同時使用稽核功能與 Microsoft Entra ID 認證。 這個挑戰存在是因為群組成員使用個人存取權杖登入,但使用者名稱仍是群組名稱。
Kusto 查詢語言(KQL)是一種強大的管線驅動、唯讀查詢語言,能查詢 Azure 服務日誌。 KQL 支援查詢 Azure 日誌,以快速分析大量資料。 本文建議使用 KQL 查詢 Azure Postgres 日誌,並從稽核日誌中擷取 Microsoft Entra ID 使用者資訊。
先決條件
- 啟用審計日誌 - 適用於 PostgreSQL 的 Azure 資料庫 中的審計日誌
- 啟用 Azure Postgres 日誌以傳送至 Azure Log Analytics - 配置並存取日誌
- 調整
log_line_prefix參數:在 [Parameters] 窗格中,將log_line_prefix設定為包含以相同順序出現的逸出字元user=%u,db=%d,session=%c,sess_time=%s,以取得所需結果。- 之前:
log_line_prefix=%t-%c- - 之後:
log_line_prefix=%t-%c-user=%u,db=%d,session=%c,sess_time=%s
- 之前:
Kusto 查詢
以下 Kusto 查詢會查詢AzureDiagnostics兩次 。
第一個子查詢會找到所有包含該字串 Microsoft Entra ID connection authorized 的行,並從這些日誌行中擷取 PrincipalName 和 SessionId。
第二個子查詢則是找到所有稽核日誌。
最後,這兩個子查詢會在 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
範例結果
所得表格如下:
| TimeGenerated | 校長名稱 | 職位名稱 | 操作類型 | SqlQuery |
|---|---|---|---|---|
| 2025-12-12T16:25:05.104Z | user@example.com | 範例群組名稱 | SELECT | 從 pg_seclabels選取 *; |
| 2025-12-12T16:25:04.000Z | user@example.com | user@example.com | SELECT | 從 pg_seclabels選取 *; |
如果使用者以群組角色登入, PrincipalName 欄位 RoleName 會顯示不同的值,就像範例的第一列所示。
這個 PrincipalName 值是用來識別登入的使用者。 該 RoleName 值識別使用者登入後所存取的 PostgreSQL 角色。
PrincipalName 是 使用者主體名稱(UPN)或AppID ,取決於使用者主體或服務主體是否登入。