الأمن السيبراني وأمن المعلومات

KQL للمبتدئين: استعلامات أولية للتحقيق في الحوادث

6 دقيقة قراءةتاريخ النشر: 5 أغسطس 2026
توضيح بصري احترافي حول KQL للمبتدئين في مجال SIEM والكشف
إجابة سريعة

KQL — Kusto Query Language — هي لغة استعلامات لقراءة وتحليل البيانات في منتجات مثل Azure Monitor و Microsoft Sentinel. يبدأ الاستعلام عادة بجدول ويستمر عبر "أنبوب" من الأوامر: تصفية الوقت والأحداث، اختيار أو إنشاء حقول، تلخيص حسب المستخدم أو الأصل، وعرض النتائج ذات الصلة بالتحقيق. مفتاح التعلم هو البدء بسؤال واحد وبناء الاستعلام خطوة بخطوة.

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

KQL هي لغة للقراءة والتحليل. ليست SQL، على الرغم من وجود مفاهيم متشابهة. تتدفق البيانات من اليسار إلى اليمين عبر Pipe — الرمز | — وكل سطر يستقبل نتيجة السطر السابق. وبهذه الطريقة يمكن بناء بحث صغير، والتحقق من النتيجة، وإضافة خطوة أخرى دون كتابة كل شيء دفعة واحدة.

تستخدم الأمثلة في الدليل أسماء جداول وحقول شائعة، ولكن Schema يختلف بين Workspaces والمصادر. قبل نسخ Query، افتح بعض السجلات، وتحقق من الحقول الفعلية، وكيّف المنطق. جميع البيانات في التمرين محاكاة.

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

يشير السطر الأول عادة إلى Table. كل Pipe يضيف Operator. على سبيل المثال، Query يعرض آخر عشر عمليات تسجيل دخول من جدول SigninLogs:

SigninLogs
| where TimeGenerated > ago(24h)
| project TimeGenerated, UserPrincipalName, IPAddress, ResultType
| sort by TimeGenerated desc
| take 10

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

الخطوة 1: تعرف على الجدول

قبل التحقيق، قم بتشغيل بعض الصفوف لرؤية Schema والأمثلة. الأمر take ممتاز للتعلم، لكنه لا يضمن أحدث الأحداث إلا إذا قمت بالترتيب. يمكنك استخدام project لتقليل العرض و project-away لإخفاء الحقول غير الضرورية.

SigninLogs
| take 5

انتبه إلى الحقول من نوع datetime و string و dynamic و int. قد يحتوي حقل dynamic على JSON أو مصفوفة ويتطلب أحيانًا parse_json أو mv-expand أو الوصول إلى خاصية داخلية. لا تفترض أن ResultType هو نفسه لكل مصدر. في وثائق الجدول أو في الأمثلة الفعلية، تحقق من معنى القيم.

الخطوة 2: التصفية باستخدام where

where هي أداة التحقيق المركزية. يمكنك التصفية حسب الوقت أو المستخدم أو IP أو النتيجة أو النص. يفضل البدء بنطاق زمني ضيق واستخدام المقارنات التي تتناسب مع نوع الحقل.

SigninLogs
| where TimeGenerated between (datetime(2026-08-01 08:00:00) .. datetime(2026-08-01 12:00:00))
| where UserPrincipalName =~ "student@contoso.example"
| project TimeGenerated, UserPrincipalName, IPAddress, AppDisplayName, ResultType

يقارن العامل =~ سلسلة نصية بغض النظر عن حالة الأحرف. للبحث الجزئي، يمكنك استخدام contains أو has أو startswith. في كثير من الحالات، يكون has أكثر كفاءة ودقة للبحث عن مصطلح كامل. تجنب التصفية الواسعة جدًا باستخدام contains عندما يمكنك استخدام حقل مدمج.

الخطوة 3: اختيار الحقول وإنشاء السياق

project يعيد فقط الحقول المحددة. extend ينشئ حقلًا جديدًا دون إزالة الحقول الموجودة. هذا مفيد للعلامات، الحسابات، والتطبيع المحلي.

SigninLogs
| where TimeGenerated > ago(24h)
| extend Outcome = iff(tostring(ResultType) == "0", "Success", "Failure")
| project TimeGenerated, UserPrincipalName, IPAddress, AppDisplayName, Outcome

عند التحقيق، فضل أسماء الحقول التي توضح المعنى. يمكنك استخدام project-rename لتغيير الاسم للعرض، ولكن لا تحجب مصدر البيانات. يجب أن تشير التذكرة الاحترافية إلى أي Table وحقل جاءت منه كل نتيجة مهمة.

الخطوة 4: summarize — تحويل الأحداث إلى صورة

summarize يجمع الأحداث ويحسب Aggregations. يمكنه العد، إيجاد الوقت الأول والأخير، جمع القيم، حساب عدد المستخدمين الفريدين، وبناء Baseline. تحدد BY المجموعات.

SigninLogs
| where TimeGenerated > ago(24h)
| summarize Attempts=count(),
            FirstSeen=min(TimeGenerated),
            LastSeen=max(TimeGenerated),
            Applications=make_set(AppDisplayName, 10)
  by UserPrincipalName, IPAddress
| sort by Attempts desc

make_set مفيد لعرض قيم مختلفة، لكن القوائم الكبيرة تكون ثقيلة وتجعل القراءة صعبة. حدد عدد العناصر. dcount يعطي عددًا تقريبيًا للقيم الفريدة وهو مناسب للتحليل الواسع؛ distinct يعيد مجموعات فريدة عندما تحتاج إلى رؤيتها.

countif و dcountif

في تحقيقات Authentication، نرغب أحيانًا في عد حالات النجاح والفشل في نفس المجموعة. countif يحسب فقط الصفوف التي تفي بالشرط:

SigninLogs
| where TimeGenerated > ago(24h)
| summarize Failures=countif(tostring(ResultType) != "0"),
            Successes=countif(tostring(ResultType) == "0"),
            UniqueUsers=dcount(UserPrincipalName)
  by IPAddress
| where Failures >= 5
| sort by Failures desc

النتيجة هي Lead. لا تثبت أن النجاح حدث بعد الفشل، وأن IP المشترك يمكن أن يكون NAT أو Proxy أو VPN. يجب فتح الأحداث الأولية وبناء Timeline قبل Classification.

نوافذ زمنية باستخدام bin

bin يجمع datetime إلى فترات ثابتة. يسمح هذا برؤية Burst من النشاط بدلاً من تلخيص يوم كامل. اختر Span حسب السلوك: قد يمتد Password Spray لفترة طويلة لتجنب العتبة؛ قد يكون Brute Force ضد حساب واحد سريعًا.

SigninLogs
| where TimeGenerated > ago(24h)
| summarize Failures=countif(tostring(ResultType) != "0"),
            Successes=countif(tostring(ResultType) == "0"),
            Users=dcount(UserPrincipalName)
  by IPAddress, bin(TimeGenerated, 30m)
| where Failures >= 10 and Successes >= 1
| sort by TimeGenerated desc

العمود TimeGenerated في النتيجة يمثل بداية الـ Bin. لرؤية الترتيب الدقيق، استخدم النتيجة لاختيار IP ونافذة، ثم قم بتشغيل Query ثانٍ يعرض الأحداث حسب الوقت.

let — لجعل الاستعلام قابلاً للقراءة

let يسمح بتسمية قيمة أو جدول مؤقت. وهو مفيد لتحديد Time range، Threshold، أو مجموعة بيانات تستخدم عدة مرات.

let Lookback = 24h;
let FailureThreshold = 10;
SigninLogs
| where TimeGenerated > ago(Lookback)
| summarize Failures=countif(tostring(ResultType) != "0"),
            Successes=countif(tostring(ResultType) == "0"),
            Users=dcount(UserPrincipalName),
            UserList=make_set(UserPrincipalName, 20)
  by IPAddress, bin(TimeGenerated, 30m)
| where Failures >= FailureThreshold and Successes >= 1
| project TimeGenerated, IPAddress, Failures, Successes, Users, UserList
| order by Failures desc

الأسماء الواضحة والتعليقات تقلل من الأخطاء. في KQL، يمكن إضافة Comment باستخدام //. يجب أن يصف Query المخطط لتحويله إلى Analytics rule هدف الكشف، الافتراضات، المصادر، الإصدار، والـ Owner.

تمرين عملي: حالات فشل تليها عملية تسجيل دخول ناجحة

في المختبر، قم بتشغيل استعلام التلخيص على بيانات محاكاة. اختر صفًا توجد فيه Failures و Successes. ثم قم بعمل Drill-down:

let TargetIP = "203.0.113.25";
let WindowStart = datetime(2026-08-01 09:00:00);
SigninLogs
| where TimeGenerated between (WindowStart .. WindowStart + 30m)
| where IPAddress == TargetIP
| extend Outcome = iff(tostring(ResultType) == "0", "Success", "Failure")
| project TimeGenerated, UserPrincipalName, IPAddress, AppDisplayName, Outcome, ResultDescription
| order by TimeGenerated asc

أجب على الأسئلة: هل أثرت حالات الفشل على مستخدم واحد أم عدة مستخدمين؟ هل النجاح ينتمي إلى نفس المستخدم؟ هل الـ IP معروف كـ VPN؟ هل تم تفعيل MFA؟ هل هناك نشاط إضافي بعد النجاح؟ فقط بعد جمع السياق يمكن تحديد ما إذا كان الأمر يتعلق بـ Password Spray، خطأ مستخدم، خدمة قديمة، أو نشاط مصرح به.

التحسين وسلامة العمل

  • قم بتصفية الوقت مبكرًا واستخدم الجدول الأكثر دقة.
  • قم بالتصفية باستخدام الحقول المدمجة بدلاً من إجراء بحث نصي على Raw data.
  • اعرض فقط الحقول المطلوبة باستخدام project.
  • تجنب join على أحجام كبيرة قبل التحقق من البدائل مثل lookup أو summarize.
  • حدد make_set و make_list.
  • اختبر Query على نطاق صغير قبل التوسيع.
  • وثّق الافتراضات، Thresholds، ومعنى قيم Result.
  • لا تحول Query إلى Detection قبل التحقق من True/False/Benign Positives.

أخطاء شائعة

  • نسخ Query دون التحقق من Schema المحلي.
  • تلخيص يوم كامل واستنتاج أن هناك سلسلة من الأحداث.
  • نسيان النطاق الزمني والمسح الضوئي لحجم كبير دون داعٍ.
  • استخدام اسم حقل مختلف بين مصدرين كما لو كان موحدًا.
  • التعامل مع Query result كدليل على هجوم.
  • إنشاء قوائم عملاقة باستخدام make_set.
  • كتابة Query طويل واحد بدلاً من التحقق من كل خطوة.

الملخص و CTA

افتح بيئة Log Analytics أو مختبرًا ببيانات محاكاة وقم ببناء Query في ثلاث خطوات: اعرض خمسة صفوف، قم بتصفية حدث واحد، ولخص حسب المستخدم أو IP. احفظ كل إصدار واشرح ما يجيب عليه. ثم انتقل إلى دليل التحقيق في Incident في Microsoft Sentinel لترى كيف يندمج Query في حالة حقيقية.

الأسئلة الشائعة

هل KQL تشبه SQL؟

هناك مفاهيم مشتركة مثل التصفية، Projection، و Aggregation، لكن بناء الجملة ونموذج الـ Pipe مختلفان. من الأفضل تعلم KQL كلغة قائمة بذاتها.

ما الفرق بين where و search؟

where تقوم بالتصفية حسب تعبير وحقول محددة وهي مفضلة في معظم التحقيقات. search يمكنها البحث عن القيم بشكل أوسع، ولكنها أحيانًا تكون أقل دقة وفعالية.

هل يمكن لـ Query تغيير أو حذف السجلات؟

تُستخدم KQL في سياق Log Analytics و Sentinel للقراءة والتحليل. يتم تنفيذ عمليات الإدارة والـ Ingestion من خلال آليات أخرى وصلاحيات مناسبة.

ما الفرق بين summarize و distinct؟

distinct يعيد مجموعات فريدة. summarize يجمع ويحسب القيم مثل count أو min أو max أو make_set.

كيف تعرف أي Table تبحث؟

ابدأ بـ Data connector ووثائق Schema، استخدم بحث Tables في Log Analytics وتحقق من أمثلة الأحداث. يمكن أن يستخدم نفس الـ Use Case جداول مختلفة بين المنظمات.

هل تريد التحقق مما إذا كان هذا المسار يناسبك؟

اترك تفاصيلك وسيتصل بك مستشار من HPI لإجراء مكالمة قصيرة لتقييم الملاءمة، دون أي التزام.

يتم تخزين بياناتك بأمان.

لدراسات SOC والأمن السيبراني ضمن برنامج Cybersecurity & AI

هل ترغب في تفاصيل البرنامج؟ اترك بياناتك وسنتواصل معك.

مقالات ذات صلة