Azure Database for PostgreSQL for Microsoft Entra ID 主体中的审核日志记录

数据库审核是组织合规性要求的重要组成部分。 通过监视目标性活动,你可以实现你的安全基线。 在 Azure Database for PostgreSQL 灵活服务器中,可以使用 pgaudit PG 扩展设置审核,如 Azure Database for PostgreSQL 中的审核日志记录中所述。

在您使用 Microsoft Entra ID 组并希望通过 Microsoft Entra ID 身份验证对各个组成员的动作进行审核时,使用审核功能是一个挑战。 这个挑战存在是因为组成员使用他们的个人访问令牌登录,但使用组名作为用户名。

Kusto 查询语言(KQL)是一种功能强大的管道驱动型只读查询语言,可用于查询 Azure 服务日志。 KQL 支持查询 Azure 日志以快速分析大量数据。 在本文中,请使用 KQL 查询 Azure Postgres 日志并从审核日志中提取Microsoft Entra ID 用户信息。

先决条件

  1. 启用审核日志记录 - Azure Database for PostgreSQL 中的审核日志记录
  2. 启用 Azure Postgres 日志发送到 Azure Log Analytics - 配置 Log Analytics
  3. log_line_prefix调整服务器参数:从“服务器参数”选项卡中,设置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 的所有行,并从这些日志行中提取 PrincipalNameSessionId
第二个子查询查找所有审核日志。
最后,这两个子查询在 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 ExampleGroupName 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,这取决于是用户主体还是服务主体进行登录。