لغة الاستعلام Kusto في Microsoft Azure Sentinel

لغة الاستعلام Kusto هي اللغة التي ستستخدمها للعمل مع البيانات ومعالجتها في Microsoft Sentinel. السجلات التي تتغذى عليها في مساحة العمل خاصتك لا تساوي الكثير إذا لم تتمكن من تحليلها والحصول على المعلومات المهمة المخفية في كل تلك البيانات. لا تتمتع لغة الاستعلام Kusto بالقوة والمرونة للحصول على هذه المعلومات فحسب، بل تتمتع أيضاً بالبساطة التي تساعدك على البدء بسرعة. إذا كان لديك خلفية في البرمجة النصية أو العمل مع قواعد البيانات، فإن الكثير من محتوى هذه المقالة سيبدو مألوفاً جداً. إذا لم يكن الأمر كذلك، فلا تقلق، لأن الطبيعة البديهية للغة تمكِّنك بسرعةٍ من البدء في كتابة استعلاماتك الخاصة والقيمة الدافعة لمؤسستك.

تقدم هذه المقالة أساسيات لغة الاستعلام Kusto، والتي تغطي بعض الوظائف وعوامل التشغيل الأكثر استخداماً، والتي يجب أن تتناول 75 إلى 80 بالمائة من الاستعلامات التي ستكتبها يوماً بعد يوم. عندما تحتاج إلى مزيد من التعمق أو لتشغيل استعلامات أكثر تقدماً، يمكنك الاستفادة من مصنف Advanced KQL لـ Microsoft Sentinel (راجع منشور المدونة التمهيدي هذا). راجع أيضاً وثائق لغة الاستعلام Kusto الرسمية بالإضافة إلى مجموعة متنوعة من الدورات التدريبية عبر الإنترنت (مثل Pluralsight).

الخلفية - لماذا لغة استعلام Kusto؟

تم إنشاء Microsoft Sentinel أعلى خدمة Azure Monitor ويستخدم مساحات عمل Log Analytics الخاصة بـ Azure Monitor لتخزين جميع بياناته. وتشمل هذه البيانات ما يلي:

  • البيانات التي تم استيعابها من مصادر خارجية في جداول معرفة مسبقاً باستخدام موصلات بيانات Microsoft Sentinel.
  • البيانات التي يتم استيعابها من مصادر خارجية في جداول مخصصة معرفة بواسطة المستخدم، باستخدام موصلات البيانات التي تم إنشاؤها خصيصاً بالإضافة إلى بعض أنواع الموصلات الجاهزة.
  • البيانات التي تم إنشاؤها بواسطة Microsoft Sentinel نفسه، الناتجة عن التحليلات التي تنشئها وتنفذها - على سبيل المثال، التنبيهات والحوادث والمعلومات المتعلقة بـ UEBA.
  • البيانات التي تم تحميلها إلى Microsoft Sentinel للمساعدة في الكشف والتحليل - على سبيل المثال، موجزات التحليل الذكي للمخاطر وقوائم المشاهدة.

تم تطوير لغة الاستعلام Kusto كجزء من خدمة Azure Data Explorer، وبالتالي تم تحسينها للبحث من خلال مخازن البيانات الضخمة في بيئة سحابية. مستوحاة من مستكشف تحت البحر الشهير Jacques Cousteau (وينطق وفقاً لذلك "koo-STOH")، تم تصميمه لمساعدتك على التعمق في محيطاتك من البيانات واستكشاف كنوزها المخفية.

تستخدم لغة الاستعلام Kusto أيضاً في Azure Monitor (وبالتالي في Microsoft Sentinel)، بما في ذلك بعض ميزات Azure Monitor الإضافية، والتي تسمح لك باسترداد البيانات وتصورها وتحليلها وتوزيعها في مخازن بيانات Log Analytics. في Microsoft Sentinel، تستخدم أدوات تستند إلى لغة الاستعلام Kusto كلما كنت تصور البيانات وتحللها وتتتبع التهديدات، سواء في القواعد والمصنفات الموجودة أو في إنشاء القواعد والمصنفات خاصتك.

نظراً لأن لغة الاستعلام Kusto هي جزء من كل ما تفعله تقريباً في Microsoft Sentinel، فإن الفهم الواضح لكيفية عملها يساعدك على الحصول على أكثر من ذلك بكثير من SIEM خاصتك.

ما هو الاستعلام؟

استعلام لغة الاستعلام Kusto هو طلب للقراءة فقط لمعالجة البيانات وإرجاع النتائج - لا يكتب أي بيانات. تعمل الاستعلامات على البيانات المنظمة في تدرج هرمي لقواعد البيانات و الجداول و الأعمدة، على غرار SQL.

ترد الطلبات بلغةٍ واضحةٍ وتستخدم نموذج تدفق البيانات المُصمم لجعل بناء الجملة سهل القراءة والكتابة والأتمتة. سنرى هذا بالتفصيل.

تتكون استعلامات لغة الاستعلام Kusto من عبارات مفصولة بفواصل منقوطة. هناك العديد من أنواع العبارات، ولكن هناك نوعان فقط يستخدمان على نطاقٍ واسعٍ سنناقشهما هنا:

  • عبارات التعبير الجدولي هي ما نعنيه عادةً عندما نتحدث عن الاستعلامات - هذه هي النص الأساسي الفعلي للاستعلام. الشيء المهم الذي يجب معرفته عن عبارات التعبير الجدولي هو أنها تقبل إدخالاً جدولياً (جدول أو تعبير جدولي آخر) وتنتج إخراجاً جدولياً. مطلوب واحد على الأقل من هذه. سيناقش معظم بقية هذه المقالة هذا النوع من العبارات.

  • تسمح لك عبارات let بإنشاء وتحديد المتغيرات والثوابت خارج نص الاستعلام، لتسهيل القراءة وتنوع الاستخدامات. هذه اختيارية وتعتمد على احتياجاتك الخاصة. سنتناول هذا النوع من العبارات في نهاية المقالة.

بيئة العرض التوضيحي

يمكنك ممارسة عبارات لغة الاستعلام Kusto - بما في ذلك تلك الموجودة في هذه المقالة - في بيئة تجريبية لـ Log Analytics في مدخل Azure. لا توجد رسوم لاستخدام بيئة التدريب هذه، ولكنك تحتاج إلى حساب Azure للوصول إليها.

استكشاف بيئة العرض التوضيحي. مثل Log Analytics في بيئة التشغيل خاصتك، يمكن استخدامه بعدة طرق:

  • اختر جدولاً لإنشاء استعلام عليه. من علامة التبويب جداول الافتراضية (الموضحة في المستطيل الأحمر في أعلى اليسار)، حدد جدولاً من قائمة الجداول المجمعة حسب المواضيع (الموضحة في أسفل اليسار). بادر بتوسيع المواضيع لمشاهدة الجداول الفردية، ويمكنك توسيع كل جدول لرؤية جميع حقوله (الأعمدة). سيؤدي النقر المزدوج فوق جدول أو اسم حقل إلى وضعه في نقطة المؤشر في نافذة الاستعلام. اكتب بقية الاستعلام باتباع اسم الجدول، كما هو موجه أدناه.

  • ابحث عن استعلام موجود للدراسة أو التعديل. حدد علامة التبويب استعلامات (الموضحة في المستطيل الأحمر في أعلى اليسار) لمشاهدة قائمة الاستعلامات المتوفرة غير المألوفة. أو حدد استعلامات من شريط الأزرار في أعلى اليمين. يمكنك استكشاف الاستعلامات التي تأتي مع Microsoft Sentinel غير المألوفة. سيؤدي النقر المزدوج فوق استعلام إلى وضع الاستعلام بأكمله في نافذة الاستعلام عند نقطة المؤشر.

    Shows the Log Analytics demo environment.

كما هو الحال في بيئة العرض التوضيحي هذه، يمكنك الاستعلام عن البيانات وتصفيتها في صفحة سجلات Microsoft Sentinel. يمكنك تحديد جدول والتنقل لأسفل لرؤية الأعمدة. يمكنك تعديل الأعمدة الافتراضية المعروضة باستخدام منتقي الأعمدة، ويمكنك تعيين النطاق الزمني الافتراضي للاستعلامات. إذا تم تعريف النطاق الزمني على نحوٍ صريحٍ في الاستعلام، فلن يكون عامل تصفية الوقت متوفراً (باللون الرمادي).

بنية الاستعلام

المكان الجيد لبدء تعلم لغة الاستعلام Kusto هو فهم بنية الاستعلام الشاملة. أول شيء ستلاحظه عند النظر إلى استعلام Kusto هو استخدام رمز الأنبوبة (|). تبدأ بنية استعلام Kusto بالحصول على بياناتك من مصدر بيانات ثم تمرير البيانات عبر "مسار"، وتوفر كل خطوة مستوى من المعالجة ثم تمرر البيانات إلى الخطوة التالية. في نهاية المسار، ستحصل على نتيجتك النهائية. في الواقع، هذا هو مسارنا:

Get Data | Filter | Summarize | Sort | Select

هذا المفهوم المتمثل في تمرير البيانات إلى أسفل المسار يجعله بنية بديهية جداً، لأنه من السهل إنشاء صورة ذهنية لبياناتك في كل خطوة.

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

إشعار

يمكنك إضافة تعليقات إلى أي سطر في استعلام بسبقها بخط مائل مزدوج (//).

SigninLogs                              // Get data
| evaluate bag_unpack(LocationDetails)  // Ignore this line for now; we'll come back to it at the end.
| where RiskLevelDuringSignIn == 'none' // Filter
   and TimeGenerated >= ago(7d)         // Filter
| summarize Count = count() by city     // Summarize
| sort by Count desc                    // Sort
| take 5                                // Select

نظراً لأن إخراج كل خطوة بمثابة إدخال للخطوة التالية، يمكن أن يحدد ترتيب الخطوات نتائج الاستعلام ويؤثر على أدائه. من المهم أن ترتب الخطوات وفقاً لما تريد الخروج منه.

تلميح

  • هناك طريقةٌ مجربةٌ جيدةٌ هي تصفية بياناتك في وقتٍ مبكرٍ، لذلك تمرر فقط البيانات ذات الصلة أسفل المسار. سيؤدي ذلك إلى زيادة الأداء بدرجةٍ كبيرةٍ والتأكد من عدم تضمين البيانات غير ذات الصلة عن طريق الخطأ في خطوات التلخيص.
  • ستشير هذه المقالة إلى بعض أفضل الممارسات الأخرى التي يجب أن تضعها في الاعتبار. للحصول على قائمة أكثر اكتمالاً، راجع أفضل ممارسات الاستعلام.

نأمل أن يكون لديك الآن تقدير للبنية الإجمالية للاستعلام في لغة الاستعلام Kusto. الآن دعونا ننظر إلى عوامل تشغيل الاستعلام الفعلية نفسها، والتي تُستخدم لإنشاء استعلام.

أنواع البيانات

قبل أن ندخل في عوامل تشغيل الاستعلام، دعونا أولاً نلقي نظرة سريعة على أنواع البيانات. كما هو الحال في معظم اللغات، يحدد نوع البيانات العمليات الحسابية والتلاعب التي يمكن تشغيلها مقابل قيمة. على سبيل المثال، إذا كانت لديك قيمة من نوع سلسلة، فلن تتمكن من إجراء حسابات حسابية مقابلها.

في لغة الاستعلام Kusto، تتبع معظم أنواع البيانات الاصطلاحات القياسية ولديك أسماء ربما رأيتها من قبل. يعرض الجدول التالي القائمة الكاملة:

جدول نوع البيانات

النوع اسم (أسماء) إضافية نوع .NET المكافئ
bool Boolean System.Boolean
datetime Date System.DateTime
dynamic System.Object
guid uuid, uniqueid System.Guid
int System.Int32
long System.Int64
real Double System.Double
string System.String
timespan Time System.TimeSpan
decimal System.Data.SqlTypes.SqlDecimal

على الرغم من أن معظم أنواع البيانات قياسية، فقد تكون أقل دراية بالأنواع مثل الديناميكية، والفترة الزمنية، والمعرف الفريد العمومي.

Dynamic تحتوي على بنية مشابهة جداً لتنسيق JSON، ولكن مع اختلاف رئيسي واحد: يمكنها تخزين أنواع البيانات الخاصة بلغة الاستعلام Kusto التي لا يمكن لتنسيق JSON التقليدي تخزينها، مثل قيمة ديناميكية متداخلة، أو الفترة الزمنية. فيما يلي مثال على نوع ديناميكي:

{
"countryOrRegion":"US",
"geoCoordinates": {
"longitude":-122.12094116210936,
"latitude":47.68050003051758
},
"state":"Washington",
"city":"Redmond"
}

الفترة الزمنية هو نوع بيانات يشير إلى مقياس الوقت مثل الساعات أو الأيام أو الثواني. لا تخلط بين الفترة الزمنية مع التاريخ والوقت، الذي يتم تقييمه إلى تاريخ ووقت فعليين، وليس مقياساً للوقت. يعرض الجدول التالي قائمة لاحقات الفترة الزمنية.

لاحقات الفترة الزمنية

الوظيفة ‏‏الوصف
D الأيام
H hours
M الدقائق
S الثواني
Ms مللي ثانية
Microsecond ميكرو ثانية
Tick نانو ثانية

Guid هو نوع بيانات يمثل 128 بت، معرف فريد عالمياً، والذي يتبع التنسيق القياسي لـ [8] - [4] - [4] - [4] - [12]، حيث يمثل كل [رقم] عدد الأحرف ويمكن أن يتراوح كل حرف من 0-9 أو a-f.

إشعار

تحتوي لغة الاستعلام Kusto على عوامل تشغيل جدولية ومقياسية. طوال بقية هذه المقالة، إذا كنت ترى ببساطة كلمة "عامل التشغيل"، يمكنك افتراض أنها تُعني عامل تشغيل جدولي، ما لم تتم الإشارة إلى خلاف ذلك.

الحصول على البيانات وتقييدها وفرزها وتصفيتها

المصطلحات الأساسية للغة الاستعلام Kusto - الأساس الذي سيسمح لك بإنجاز الغالبية العظمى من مهامك - هي مجموعة من عوامل التشغيل لتصفية بياناتك وفرزها واختيارها. تتطلب المهام المتبقية التي ستحتاج إلى القيام بها توسيع معرفتك باللغة لتلبية احتياجاتك الأكثر تقدماً. دعونا نوسع قليلاً على بعض الأوامر التي استخدمناها في مثالنا أعلاه وننظر إلى take و sort و where.

لكل عامل من عوامل التشغيل هذه، سنفحص استخدامه في مثال SigninLogs السابق، ونتعلم إما تلميحاً مفيداً أو أفضل ممارسة.

الحصول على بيانات

يحدد السطر الأول من أي استعلام أساسي الجدول الذي تريد العمل معه. في حالة Microsoft Sentinel، من المحتمل أن يكون هذا اسم نوع سجل في مساحة العمل خاصتك، مثل SigninLogs أو SecurityAlert أو CommonSecurityLog. على سبيل المثال:

SigninLogs

لاحظ أنه في لغة الاستعلام Kusto تكون أسماء السجلات حساسة لحالة الأحرف، لذلك SigninLogs و signinLogs سيتم تفسيرها على نحوٍ مختلفٍ. انتبه عند اختيار الأسماء للسجلات المخصصة خاصتك، بحيث يمكن التعرف عليها بسهولة وليست مشابهة جداً لسجل آخر.

الحد من البيانات: الحد / take

يتم استخدام عامل التشغيل take (وعامل تشغيل الحد المتطابق) للحد من نتائجك عن طريق إرجاع عدد معين فقط من الصفوف. يتبعه عدد صحيح يحدد عدد الصفوف التي يجب إرجاعها. عادةً، يتم استخدامه في نهاية استعلام بعد تحديد ترتيب الفرز خاصتك، وفي مثل هذه الحالة سيعيد العدد المحدد من الصفوف في أعلى الترتيب الذي تم فرزه.

يمكن أن يكون استخدام take سابقاً في الاستعلام مفيداً لاختبار استعلام، عندما لا تريد إرجاع مجموعات بيانات كبيرة. ومع ذلك، إذا وضعت العملية take قبل أي sort عمليات، take فسيتم إرجاع الصفوف المحددة عشوائياً - وربما مجموعة مختلفة من الصفوف في كل مرة يتم فيها تشغيل الاستعلام. وإليك مثال على استخدام take:

SigninLogs
      | take 5

Screenshot of results of take operator.

تلميح

عند العمل على استعلام العلامة التجارية الجديدة حيث قد لا تعرف كيف سيبدو الاستعلام، قد يكون من المفيد وضع عبارة take في البداية للحد بشكل مصطنع من مجموعة البيانات خاصتك للمعالجة والتجريب بدرجةٍ أسرع. بمجرد أن تكون راضياً عن الاستعلام الكامل، يمكنك إزالة الخطوة الأولية take.

فرز البيانات: ترتيب / الفرز

يتم استخدام عامل تشغيل الفرز (وعامل تشغيل الطلب المتطابق) لفرز البيانات حسب عمود محدد. في المثال التالي، بادرنا بترتيب النتائج حسب TimeGenerated وتعيين اتجاه الترتيب إلى تنازلي مع المعلمة desc، مع وضع أعلى القيم أولاً؛ لترتيب تصاعدي سنستخدم asc.

إشعار

الاتجاه الافتراضي للفرز تنازلي، لذلك من الناحية الفنية يجب عليك فقط تحديد ما إذا كنت تريد الفرز بترتيب تصاعدي. ومع ذلك، فإن تحديد اتجاه الفرز في أي حال سيجعل الاستعلام أكثر قابليةً للقراءة.

SigninLogs
| sort by TimeGenerated desc
| take 5

كما ذكرنا، وضعنا عامل التشغيل sort قبل عامل التشغيل take. نحن بحاجة إلى الفرز أولاً للتأكد من أننا نحصل على السجلات الخمسة المناسبة.

Screenshot of results of sort operator, with take limit.

Top

يسمح لنا عامل التشغيل العلوي بدمج عمليات sort و take في عامل تشغيل واحد:

SigninLogs
| top 5 by TimeGenerated desc

في الحالات التي يكون فيها لسجلين أو أكثر نفس القيمة في العمود الذي تفرز حسبه، يمكنك إضافة المزيد من الأعمدة للفرز حسبها. أضف أعمدة فرز إضافية في قائمة مفصولة بفواصل، تقع بعد عمود الفرز الأول، ولكن قبل الكلمة الأساسية لترتيب الفرز. على سبيل المثال:

SigninLogs
| sort by TimeGenerated, Identity desc
| take 5

الآن، إذا كان TimeGenerated هو نفسه بين سجلات متعددة، فسيحاول بعد ذلك الفرز حسب القيمة في عمود الهوية.

إشعار

متى تستخدم sort و take، ومتى تستخدم top

  • إذا كنت تفرز على حقل واحد فقط، فاستخدم top، لأنه يوفر أداء أفضل من الجمع بين sort و take.

  • إذا كنت بحاجة إلى الفرز على أكثر من حقلٍ واحدٍ (كما في المثال الأخير أعلاه)، top فلا يمكنك القيام بذلك، لذلك يجب عليك استخدام sort و take.

تصفية البيانات: حيث

يمكن القول إن عامل التشغيل حيث هو عامل التشغيل الأكثر أهمية، لأنه مفتاح التأكد من أنك تعمل فقط مع المجموعة الفرعية من البيانات ذات الصلة بالسيناريو خاصتك. يجب أن تبذل قصارى جهدك لتصفية بياناتك في وقتٍ مبكرٍ من الاستعلام قدر الإمكان لأن القيام بذلك سيؤدي إلى تحسين أداء الاستعلام عن طريق تقليل كمية البيانات التي تحتاج إلى معالجة في الخطوات اللاحقة؛ كما أنه يضمن أنك تجري فقط العمليات الحسابية على البيانات المطلوبة. انظر هذا المثال:

SigninLogs
| where TimeGenerated >= ago(7d)
| sort by TimeGenerated, Identity desc
| take 5

يحدد عامل التشغيل where متغيراً وعامل مقارنة (عددي) وقيمة. في حالتنا، كنا >= ندل على أن القيمة في العمود TimeGenerated يجب أن تكون أكبر من (أي، بعد) أو تساوي قبل سبعة أيام.

هناك نوعان من عوامل المقارنة في لغة الاستعلام Kusto: سلسلة وعددية. يعرض الجدول التالي القائمة الكاملة لعوامل التشغيل الرقمية:

عوامل التشغيل العددية

عامل ‏‏الوصف
+ الجمع
- الطرح
* الضرب
/ القسم
% Modulo
< أقل من
> أكبر من
== يساوي
!= لا يساوي
<= أقل من أو يساوي
>= أكبر من أو يساوي
in يساوي أحد العناصر
!in لا يساوي أي من العناصر

قائمة عوامل تشغيل السلسلة هي قائمة أطول بكثير لأنها تحتوي على تباديل لحساسية الحالة ومواقع السلسلة الفرعية والبادئات واللاحقات وأكثر من ذلك بكثير. عامل التشغيل == هو عامل تشغيل رقمي وسلسلة، مما يُعني أنه يمكن استخدامه لكل من الأرقام والنص. على سبيل المثال، سيكون كلا العبارات التالية صالحاً حيث العبارات:

  • | where ResultType == 0
  • | where Category == 'SignInLogs'

تلميح

أفضل الممارسات: في معظم الحالات، ستحتاج على الأرجح إلى تصفية بياناتك حسب أكثر من عمود واحد، أو تصفية العمود نفسه بأكثر من طريقة واحدة. في هذه الحالات، هناك اثنتان من أفضل الممارسات التي يجب أن تضعها في الاعتبار.

يمكنك دمج عبارات متعددة where في خطوة واحدة باستخدام الكلمة الأساسية و. على سبيل المثال:

SigninLogs
| where Resource == ResourceGroup
    and TimeGenerated >= ago(7d)

عندما يكون لديك عوامل تصفية متعددة مرتبطة في عبارة واحدة where باستخدام الكلمة الأساسية و، كما هو موضح أعلاه، ستحصل على أداء أفضل عن طريق وضع عوامل التصفية التي تشير إلى عمود واحد فقط أولاً. لذلك، فإن أفضل طريقة لكتابة الاستعلام أعلاه هي:

SigninLogs
| where TimeGenerated >= ago(7d)
    and Resource == ResourceGroup

في هذا المثال، يشير عامل التصفية الأول إلى عمودٍ واحدٍ (TimeGenerated)، بينما يشير الثاني إلى عمودين (Resource و ResourceGroup).

تلخيص البيانات

التلخيص هو أحد أهم عوامل التشغيل الجدولية في لغة الاستعلام Kusto، ولكنه أيضاً أحد عوامل التشغيل الأكثر تعقيداً لمعرفة ما إذا كنت جديداً على الاستعلام عن اللغات بوجهٍ عامٍ. تتمثل مهمة summarize في أخذ جدول بيانات وإخراج جدول جديد يتم تجميعه بواسطة عمود واحد أو أكثر.

بنية عبارة التلخيص

البنية الأساسية لعبارة summarize هي كما يلي:

| summarize <aggregation> by <column>

على سبيل المثال، قد يرجع ما يلي عدد السجلات لكل قيمة CounterName في جدول Perf:

Perf
| summarize count() by CounterName

Screenshot of results of summarize operator with count aggregation.

نظراً لأن إخراج summarize هو جدول جديد، فإن أي أعمدة غير محددة على نحوٍ صريحٍ في العبارة summarize فلن يتم تمرير إلى أسفل المسار. لتوضيح هذا المفهوم، ضع في اعتبارك هذا المثال:

Perf
| project ObjectName, CounterValue, CounterName
| summarize count() by CounterName
| sort by ObjectName asc

في السطر الثاني، نحدد أننا نهتم فقط بأعمدة ObjectName و CounterValue و CounterName. ثم لخصنا للحصول على عدد السجلات بواسطة CounterName وأخيراً، نحاول فرز البيانات بترتيب تصاعدي استناداً إلى العمود ObjectName. لسوء الحظ، سيفشل هذا الاستعلام مع وجود خطأ (يُشير إلى أن ObjectName غير معروف) لأننا عندما لخصنا، بادرنا بتضمين عمودي Count و CounterName فقط في جدولنا الجديد. لتجنب هذا الخطأ، يمكننا ببساطة إضافة ObjectName إلى نهاية خطوتنا summarize، مثل هذا:

Perf
| project ObjectName, CounterValue , CounterName
| summarize count() by CounterName, ObjectName
| sort by ObjectName asc

ستكون طريقة قراءة السطر summarize في رأسك هي: تلخيص عدد السجلات حسب CounterName، والتجميع حسب ObjectName. يمكنك متابعة إضافة أعمدة مفصولة بفواصل إلى نهاية العبارة summarize.

Screenshot of results of summarize operator with two arguments.

بناءً على المثال السابق، إذا أردنا تجميع أعمدة متعددة في نفس الوقت، يمكننا تحقيق ذلك عن طريق إضافة تجميعات إلى عامل التشغيل summarize مفصولة بفواصل. في المثال أدناه، نحصل ليس فقط على عدد جميع السجلات ولكن أيضًا على مجموع القيم في عمود CounterValue عبر جميع السجلات (التي تتطابق مع أي عوامل تصفية في الاستعلام):

Perf
| project ObjectName, CounterValue , CounterName
| summarize count(), sum(CounterValue) by CounterName, ObjectName
| sort by ObjectName asc

Screenshot of results of summarize operator with multiple aggregations.

إعادة تسمية الأعمدة المجمعة

يبدو أن هذا الوقت مناسب للتحدث عن أسماء الأعمدة لهذه الأعمدة المجمعة. في بداية هذا القسم، قلنا إن عامل التشغيل summarize يأخذ جدول بيانات وينتج جدولًا جديدًا، وستستمر الأعمدة التي تحددها في العبارة summarize فقط في المسار. لذلك، إذا كنت تريد تشغيل المثال أعلاه، فإن الأعمدة الناتجة للتجميع لدينا ستكون count_ و sum_CounterValue.

سينشئ محرك Kusto تلقائياً اسم عمود دون الحاجة إلى أن نكون صريحين، ولكن في كثير من الأحيان، ستجد أنك تفضل أن يكون للعمود الجديد اسم أكثر صداقةً. يمكنك بسهولة إعادة تسمية العمود في العبارة summarize عن طريق تحديد اسم جديد، متبوعاً بالتجميع =، كما يلي:

Perf
| project ObjectName, CounterValue , CounterName
| summarize Count = count(), CounterSum = sum(CounterValue) by CounterName, ObjectName
| sort by ObjectName asc

الآن، سيتم تسمية أعمدتنا الملخصة Count و CounterSum.

Screenshot of friendly column names for aggregations.

يوجد عامل التشغيل summarize أكثر بكثير مما يمكننا تغطيته هنا، ولكن يجب أن تستثمر الوقت لتعلمه لأنه مكون أساسي لأي تحليل بيانات تخطط لإجراءه على بيانات Microsoft Sentinel خاصتك.

مرجع التجميع

هي العديد من دالات التجميع، ولكن بعض أكثرها استخداماً هي sum() و count() و avg(). فيما يلي قائمة جزئية (راجع القائمة الكاملة):

دوال التجميع

الوظيفة ‏‏الوصف
arg_max() إرجاع تعبير واحد أو أكثر عند تكبير الوسيطة
arg_min() إرجاع تعبير واحد أو أكثر عند تصغير الوسيطة
avg() إرجاع متوسط القيمة عبر المجموعة
buildschema() إرجاع الحد الأدنى من المخطط الذي يعترف بجميع قيم الإدخال الديناميكي
count() إرجاع عدد المجموعة
countif() إرجاع العدد مع دالة تقييم المجموعة
dcount() إرجاع العدد المميز التقريبي لعناصر المجموعة
make_bag() إرجاع حقيبة خصائص للقيم الديناميكية داخل المجموعة
make_list() إرجاع قائمة بجميع القيم داخل المجموعة
make_set() إرجاع مجموعة من القيم المميزة داخل المجموعة
max() إرجاع الحد الأقصى للقيمة عبر المجموعة
min() إرجاع الحد الأدنى للقيمة عبر المجموعة
percentiles() إرجاع القيمة المئوية التقريبية للمجموعة
stdev() إرجاع الانحراف المعياري عبر المجموعة
sum() إرجاع مجموع العناصر داخل المجموعة
take_any() إرجاع قيمة عشوائية غير فارغة للمجموعة
variance() إرجاع التباين عبر المجموعة

تحديد: إضافة الأعمدة وإزالتها

عندما تبدأ العمل أكثر مع الاستعلامات، قد تجد أن لديك معلومات أكثر مما تحتاج إليه عن مواضيعك (أي عدد كبير جداً من الأعمدة في جدولك). أو قد تحتاج إلى معلومات أكثر مما لديك (أي أنك تحتاج إلى إضافة عمود جديد يحتوي على نتائج تحليل الأعمدة الأخرى). لنلقِ نظرةً على عددٍ قليلٍ من عوامل التشغيل الرئيسية للمعالجة بالعمود.

المشروع و المشروع االخارجي

المشروع مكافئ تقريباً لعبارات تحديد العديد من اللغات. يسمح لك باختيار الأعمدة التي يجب الاحتفاظ بها. سيتطابق ترتيب الأعمدة التي تم إرجاعها مع ترتيب الأعمدة التي تدرجها في عبارتك project، كما هو موضح في هذا المثال:

Perf
| project ObjectName, CounterValue, CounterName

Screenshot of results of project operator.

كما يمكنك أن تتخيل، عندما تعمل مع مجموعات بيانات واسعة جداً، قد يكون لديك الكثير من الأعمدة التي تريد الاحتفاظ بها، وقد يتطلب تحديدها كلها بالاسم الكثير من الكتابة. بالنسبة إلى هذه الحالات، لديك مشروع خارجي، مما يتيح لك تحديد الأعمدة التي يجب إزالتها، بدلاً من الأعمدة التي يجب الاحتفاظ بها، على النحو التالي:

Perf
| project-away MG, _ResourceId, Type

تلميح

قد يكون من المفيد استخدام project في موقعين في استعلاماتك، في البداية ومرة أخرى في النهاية. يمكن أن يساعد استخدام project في وقتٍ مبكر من استعلامك في تحسين الأداء عن طريق إزالة أجزاء كبيرة من البيانات التي لا تحتاج إلى تمرير المسار. يتيح لك استخدامه مرةً أخرى في النهاية التخلص من أي أعمدة قد تكون تم إنشاؤها في الخطوات السابقة وغير مطلوبة في الإخراج النهائي.

توسيع

توسيع لإنشاء عمود محسوب جديد. يمكن أن يكون هذا مفيداً عندما تريد إجراء عملية حسابية مقابل الأعمدة الموجودة ورؤية الإخراج لكل صف. لنلقِ نظرةً على مثالٍ بسيطٍ حيث نحسب عموداً جديداً يُسمى Kbytes، والذي يمكننا حسابه عن طريق ضرب قيمة MB (في عمود الكمية الموجود) في 1024.

Usage
| where QuantityUnit == 'MBytes'
| extend KBytes = Quantity * 1024
| project ResourceUri, MBytes=Quantity, KBytes

في السطر الأخير في عبارتنا project، بادرنا بإعادة تسمية عمود الكمية إلى Mbytes، حتى نتمكن من معرفة وحدة القياس ذات الصلة بكل عمودٍ بسهولةٍ.

Screenshot of results of extend operator.

تجدر الإشارة إلى أن extend يعمل أيضاً مع الأعمدة المحسوبة بالفعل. على سبيل المثال، يمكننا إضافة عمود آخر يُسمى بايت يتم حسابه من Kbytes:

Usage
| where QuantityUnit == 'MBytes'
| extend KBytes = Quantity * 1024
| extend Bytes = KBytes * 1024
| project ResourceUri, MBytes=Quantity, KBytes, Bytes

Screenshot of results of two extend operators.

ربط الجداول

يمكن تنفيذ جزء كبير من عملك في Microsoft Sentinel باستخدام نوع سجل واحد، ولكن هناك أوقات تريد فيها ربط البيانات معاً أو إجراء بحث مقابل مجموعة أخرى من البيانات. مثل معظم لغات الاستعلام، تقدم لغة الاستعلام Kusto بعض عوامل التشغيل المستخدمة لتنفيذ أنواع مختلفة من الصلات. في هذا القسم، سننظر في عوامل التشغيل الأكثر استخداماً، union و join.

الاتحاد

التوحيد يأخذ جدولين أو أكثر ببساطة ويعيد جميع الصفوف. على سبيل المثال:

OfficeActivity
| union SecurityEvent

سيؤدي ذلك إلى إرجاع كافة الصفوف من كلٍ من جدولي OfficeActivity و SecurityEvent. Union يقدم بعض المعلمات التي يمكن استخدامها لضبط كيفية تصرف التوحيد. اثنان من أكثرها فائدة هي المعلمة withsource و kind:

OfficeActivity
| union withsource = SourceTable kind = inner SecurityEvent

تتيح لك المعلمة withsource تحديد اسم عمود جديد ستكون قيمته في صفٍ معينٍ هو اسم الجدول الذي جاء منه الصف. في المثال أعلاه، بادرنا بتسمية العمود SourceTable، واستناداً إلى الصف، ستكون القيمة إما OfficeActivity أو SecurityEvent.

المعلمة الأخرى التي حددناها كانت kind، والتي تحتوي على خيارين: داخلي أو خارجي. في المثال أعلاه، حددنا داخلي، مما يُعني أن الأعمدة الوحيدة التي سيتم الاحتفاظ بها أثناء التوحيد هي تلك الموجودة في كلا الجدولين. بدلاً من ذلك، إذا كنا قد حددنا خارجي (وهي القيمة الافتراضية)، فسيتم إرجاع جميع الأعمدة من كلا الجدولين.

الانضمام

الانضمام يعمل بشكلٍ مشابهٍ لـ union، باستثناء أنه بدلاً من ربط الجداول لإنشاء جدول جديد، نقوم بالانضمام إلى صفوف لإنشاء جدول جديد. مثل معظم لغات قاعدة البيانات، هناك أنواع متعددة من الصلات التي يمكنك تنفيذها. بناء الجملة العام لـ join هو:

T1
| join kind = <join type>
(
               T2
) on $left.<T1Column> == $right.<T2Column>

بعد عامل التشغيل join، نحدد المعلمة kind الخاصة بالانضمام التي نريد تنفيذها متبوعة بقوس مفتوح. ضمن الأقواس يوجد المكان الذي تحدد فيه الجدول الذي تريد الانضمام إليه، بالإضافة إلى أي عبارات استعلام أخرى على الجدول الذي ترغب في إضافته. بعد قوس الإغلاق، نستخدم الكلمة الأساسية on متبوعة بالمعلمة ($left.< اليسرى والكلمة الأساسية columnName>) واليمين ($right.< columnName>) أعمدة مفصولة بعامل التشغيل ==. فيما يلي مثال على صلة داخلية:

OfficeActivity
| where TimeGenerated >= ago(1d)
    and LogonUserSid != ''
| join kind = inner (
    SecurityEvent
    | where TimeGenerated >= ago(1d)
        and SubjectUserSid != ''
) on $left.LogonUserSid == $right.SubjectUserSid

إشعار

إذا كان كلا الجدولين يحملان نفس الاسم للأعمدة التي تجري صلة عليها، فلن تحتاج إلى استخدام $left و $right؛ بدلاً من ذلك، يمكنك فقط تحديد اسم العمود. ومع ذلك، فإن استخدام $left و $right أكثر وضوحاً ويعتبر عموماً ممارسةً جيدةً.

للرجوع إليها، يعرض الجدول التالي قائمة بالأنواع المتوفرة من الصلات.

أنواع الصلات

نوع الصلة ‏‏الوصف
inner إرجاع واحد لكل مجموعة من الصفوف المتطابقة من كلا الجدولين.
innerunique إرجاع صفوف من الجدول الأيسر بقيمٍ مميزةٍ في الحقل المرتبط لها تطابق في الجدول الأيمن.
هذا هو نوع الصلة الافتراضي غير المحدد.
leftsemi إرجاع كافة السجلات من الجدول الأيسر التي تحتوي على تطابق في الجدول الأيمن.
سيتم إرجاع الأعمدة من الجدول الأيسر فقط.
rightsemi إرجاع كافة السجلات من الجدول الأيمن التي تحتوي على تطابق في الجدول الأيسر.
سيتم إرجاع الأعمدة من الجدول الأيمن فقط.
leftanti/
leftantisemi
إرجاع كافة السجلات من الجدول الأيسر التي تحتوي على تطابق في الجدول الأيمن.
سيتم إرجاع الأعمدة من الجدول الأيسر فقط.
rightanti/
rightantisemi
إرجاع كافة السجلات من الجدول الأيمن التي تحتوي على تطابق في الجدول الأيسر.
سيتم إرجاع الأعمدة من الجدول الأيمن فقط.
leftouter إرجاع كافة السجلات من الجدول الأيسر. بالنسبة للسجلات التي ليس لها تطابق في الجدول الصحيح، ستكون قيم الخلايا خالية.
rightouter إرجاع كافة السجلات من الجدول الأيمن. بالنسبة للسجلات التي ليس لها تطابق في الجدول الأيسر، ستكون قيم الخلايا خالية.
fullouter إرجاع كافة السجلات من كلٍ من الجدولين الأيمن والأيسر، مطابقاً أم لا.
ستكون القيم غير المتطابقة خالية.

تلميح

من أفضل الممارسات أن يكون لديك أصغر جدول على اليسار. في بعض الحالات، يمكن أن يمنحك اتباع هذه القاعدة فوائد أداء ضخمة، اعتماداً على أنواع الصلات التي تنفذها وحجم الجداول.

التقييم

قد تتذكر أنه في المثال الأول، رأينا عامل تشغيل التقييم على أحد الأسطر. عامل التشغيل evaluate أقل استخداماً من تلك التي لمسناها سابقاً. ومع ذلك، فإن معرفة كيفية عمل عامل التشغيل evaluate تستحق وقتك. مرةً أخرى، إليك هذا الاستعلام الأول، حيث سترى evaluate في السطر الثاني.

SigninLogs
| evaluate bag_unpack(LocationDetails)
| where RiskLevelDuringSignIn == 'none'
   and TimeGenerated >= ago(7d)
| summarize Count = count() by city
| sort by Count desc
| take 5

يسمح لك عامل التشغيل هذا باستدعاء المكونات الإضافية المتوفرة (الوظائف المُضمَّنة أساساً). تركز العديد من هذه المكونات الإضافية على علم البيانات، مثل التكتل التلقائي، و diffpatterns، و sequence_detect، ما يسمح لك بإجراء تحليل متقدم واكتشاف الحالات الإحصائية الشاذة والقيم الخارجية.

يُسمى المكون الإضافي المستخدم في المثال أعلاه bag_unpack، ويجعل من السهل جداً أخذ مجموعة من البيانات الديناميكية وتحويلها إلى أعمدة. تذكر أن البيانات الديناميكية هي نوع بيانات يشبه إلى حدٍ كبيرٍ JSON، كما هو موضح في هذا المثال:

{
"countryOrRegion":"US",
"geoCoordinates": {
"longitude":-122.12094116210936,
"latitude":47.68050003051758
},
"state":"Washington",
"city":"Redmond"
}

في هذه الحالة، أردنا تلخيص البيانات حسب المدينة، ولكن يتم تضمين المدينة كخاصية داخل عمود LocationDetails. لاستخدام خاصية المدينة في استعلامنا، كان علينا أولاً تحويلها إلى عمود باستخدام bag_unpack.

بالعودة إلى خطوات المسارات الأصلية، رأينا ما يلي:

Get Data | Filter | Summarize | Sort | Select

الآن بعد أن نظرنا في عامل التشغيل evaluate، يمكننا أن نرى أنه يمثل مرحلةً جديدةً في المسار، والتي تبدو الآن كما يلي:

Get Data | Parse | Filter | Summarize | Sort | Select

هناك العديد من الأمثلة الأخرى للمشغلين والوظائف التي يمكن استخدامها لتحليل مصادر البيانات في تنسيق أكثر قابلية للقراءة والتلاعب. يمكنك التعرف عليها - وبقية لغة استعلام Kusto - في الوثائق الكاملة وفي المصنف.

عبارات Let

الآن بعد أن غطينا العديد من عوامل التشغيل الرئيسية وأنواع البيانات، دعنا نختتم بعبارة let، وهو طريقة رائعة لتسهيل قراءة الاستعلامات وتحريرها وصيانتها.

Let لك بإنشاء متغير وتعيينه، أو تعيين اسم إلى تعبير. يمكن أن يكون هذا التعبير قيمةً واحدةً، ولكنه قد يكون أيضاً استعلاماً كاملاً. إليك مثال بسيط:

let aWeekAgo = ago(7d);
SigninLogs
| where TimeGenerated >= aWeekAgo

هنا، حددنا اسماً لـ aWeekAgo وقمنا بتعيينه ليكون مساوياً لإخراج دالة الفترة الزمنية، والتي ترجع قيمة التاريخ والوقت. ثم نُنهي عبارة let بفواصل منقوطة. الآن لدينا متغير جديد يُسمى AWeekAgo يمكن استخدامه في أي مكان في استعلامنا.

كما ذكرنا للتو، يمكنك استخدام عبارة let لأخذ استعلام كامل وإعطاء النتيجة اسماً. نظراً لأنه يمكن استخدام نتائج الاستعلام، كونها تعبيرات جدولية، كإدخالات للاستعلامات، يمكنك التعامل مع هذه النتيجة المسماة كجدول لأغراض تشغيل استعلام آخر عليها. فيما يلي تعديل طفيف على المثال السابق:

let aWeekAgo = ago(7d);
let getSignins = SigninLogs
| where TimeGenerated >= aWeekAgo;
getSignins

في هذه الحالة، أنشأنا عبارة let ثانية، حيث قمنا بتضمين استعلامنا بالكامل في متغيرٍ جديدٍ يُسمى getSignins. تماماً كما في السابق، نُنهي عبارة let الثانية بفواصل منقوطة. ثم نستدعي المتغير على السطر النهائي، والذي سيشغِّل الاستعلام. لاحظ أننا تمكَّنا من استخدام aWeekAgo في عبارة let الثانية. وذلك لأننا حددناه في السطر السابق؛ إذا أردنا تبديل عبارات let بحيث يأتي getSignins أولاً، فسنحصل على خطأ.

الآن يمكننا استخدام getSignins كأساس لاستعلام آخر (في نفس النافذة):

let aWeekAgo = ago(7d);
let getSignins = SigninLogs
| where TimeGenerated >= aWeekAgo;
getSignins
| where level >= 3
| project IPAddress, UserDisplayName, Level

عبارات Letتمنحك المزيد من القوة والمرونة في المساعدة على تنظيم استعلاماتك. عبارات Let يمكنها تعريف القيم العددية والجدارية بالإضافة إلى إنشاء وظائف معرفة بواسطة المستخدم. إنها مفيدة حقاً عندما تنظِّم استعلامات أكثر تعقيداً قد تجري صلات متعددة.

الخطوات التالية

على الرغم من أن هذه المقالة قد خدشت السطح بالكاد، فلديك الآن الأساس اللازم، وقمنا بتغطية الأجزاء التي ستستخدمها في أغلب الأحيان لإنجاز عملك في Microsoft Sentinel.

مصنف Advanced KQL لـ Microsoft Sentinel

استفِد من مصنف لغة استعلام Kusto مباشرةً في Microsoft Sentinel نفسه - مصنف Advanced KQL لـ Microsoft Sentinel. فهو يمنحك تعليمات خطوةً بخطوةٍ وأمثلة للعديد من المواقف التي من المحتمل أن تواجهها أثناء عمليات الأمان اليومية، كما يوجهك إلى الكثير من الأشياء غير المألوفة الجاهزة. أمثلة لقواعد التحليلات والمصنفات وقواعد التتبع والمزيد من العناصر التي تستخدم استعلامات Kusto. تشغيل هذا المصنف من جزء المصنفات في Microsoft Sentinel.

مصنف Advanced KQL Framework - تمكنك من أن تصبح KQL-savvy هو منشور مدونة ممتاز يوضح لك كيفية استخدام هذا المصنف.

موارد إضافية

راجع هذه المجموعة من موارد التعلم والتدريب والمهارات لتوسيع وتعميق معرفتك بـ Kusto Query Language.