データベース監査は、組織のコンプライアンス要件の重要なコンポーネントです。 対象となるアクティビティを監視することで、セキュリティ ベースラインを実現できます。 Azure Database for PostgreSQL フレキシブル サーバーでは、「 Azure Database for PostgreSQL でのログ記録の監査」の説明に従って、pgaudit PG 拡張機能を使用して監査を設定できます。
1 つの課題は、Microsoft Entra ID グループを使用していて、個々のグループ メンバーのアクションを監査する場合に、Microsoft Entra ID 認証と共に監査機能を使用することです。 この課題は、グループ メンバーが個人用アクセス トークンを使用してサインインするが、グループ名をユーザー名として使用するためです。
Kusto クエリ言語 (KQL) は、Azure サービス ログのクエリを可能にする、パイプライン駆動型の読み取り専用の強力なクエリ言語です。 KQL では、大量のデータをすばやく分析するための Azure ログのクエリがサポートされています。 この記事では、KQL を使用して Azure Postgres ログのクエリを実行し、監査ログから Microsoft Entra ID ユーザー情報を抽出します。
[前提条件]
- 監査ログの有効化 - Azure Database for PostgreSQL での監査ログ
- Azure Postgres ログを Azure Log Analytics に送信できるようにする - ログの構成とアクセス
-
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 を 2 回クエリします。
最初のサブクエリは、文字列Microsoft Entra ID connection authorizedを含むすべての行を検索し、これらのログ行からPrincipalNameと共にSessionIdを抽出します。
2 番目のサブクエリでは、すべての監査ログが検索されます。
最後に、これら 2 つのサブクエリが 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
例の結果
結果のテーブルは次のようになります。
| タイムジェネレイテッド | プリンシパルネーム | 役割名 | 操作タイプ | 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 のいずれかです。