تسجيل التدقيق لكيانات Microsoft Entra ID في خادم مرن قاعدة بيانات Azure لـ PostgreSQL

تعد تدقيقات قواعد البيانات جزءا مهما من متطلبات الامتثال لمنظمتك. من خلال مراقبة الأنشطة المستهدفة، يمكنك تحقيق مستوى الأمان الأساسي لديك. في قاعدة بيانات Azure لـ PostgreSQL مرن سيرفر، يمكنك إعداد التدقيقات باستخدام إضافة pgaudit PG، كما هو موضح في تسجيل التدقيق في قاعدة بيانات Azure لـ PostgreSQL.

أحد التحديات هو استخدام ميزة التدقيق إلى جانب مصادقة Microsoft Entra ID عند استخدام مجموعات Microsoft Entra ID وترغب في تدقيق إجراءات أعضاء المجموعة الفردية. يوجد هذا التحدي لأن أعضاء المجموعة يسجلون الدخول باستخدام رموز الوصول الشخصية لكنهم يستخدمون اسم المجموعة كاسم مستخدم.

لغة استعلام كوستو (KQL) هي لغة استعلام قوية تعتمد على خطوط الأنابيب وتقرأ فقط تتيح الاستعلام من سجلات خدمات أزور. يدعم KQL استعلام سجلات Azure لتحليل حجم كبير من البيانات بسرعة. في هذه المقالة، استخدم KQL للاستعلام عن سجلات Azure Postgres واستخراج معلومات مستخدم Microsoft Entra ID من سجلات التدقيق.

Prerequisites

  1. تمكين تسجيل التدقيق - تسجيل التدقيق في قاعدة بيانات Azure لـ PostgreSQL
  2. تمكين Azure سجلات Postgres لإرسالها إلى Azure 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

استعلام كوستو التالي يطرح الاستفسار عن 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 اسم المدير RoleName OperationType 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 حسب ما إذا كان العميل الرئيسي أو الخدمة يسجل دخول.