Язык запросов Kusto в Microsoft Sentinel
язык запросов Kusto — это язык, используемый для работы с данными и управления ими в Microsoft Sentinel. Журналы, которые передаются в рабочую область, не имеют особой пользы, если их нельзя анализировать и получать важные сведения, скрытые в данных. Язык запросов Kusto не только предоставляет эффективные и гибкие возможности для извлечения этой информации, но и позволяет быстро приступить к работе благодаря своей простоте. Если у вас есть фон в скриптах или работе с базами данных, содержимое этой статьи должно быть знакомо. Если нет, не беспокойтесь: интуитивно понятная природа языка позволит быстро начать писать собственные запросы и получать ценность для вашей организации.
В этой статье представлены основы язык запросов Kusto, охватывающие некоторые из наиболее используемых функций и операторов, которые должны решать 75–80 процентов запросов, которые пользователи записывают день в день. Если вам нужна более подробная глубина или выполнение более сложных запросов, вы можете воспользоваться новыми книгами Advanced KQL для Microsoft Sentinel (см. эту вводную запись блога). См. также официальную язык запросов Kusto документацию и различные онлайн-курсы (например, Pluralsight).
Зачем нужен язык запросов Kusto
Решение Microsoft Sentinel построено на основе службы Azure Monitor и использует ее рабочие области Log Analytics для хранения всех данных. К этим данным относятся следующие:
- данные, принятые из внешних источников в предварительно определенные таблицы с помощью соединителей данных Microsoft Sentinel;
- данные, полученные из внешних источников в пользовательские таблицы, с помощью настраиваемых соединителей данных и некоторых типов внешних соединителей.
- данные, созданные самим решением Microsoft Sentinel в результате анализа, например оповещения, инциденты и сведения, связанные с UEBA;
- данные, переданные в Microsoft Sentinel с целью помочь в обнаружении и анализе, например веб-каналы аналитики угроз и списки видео к просмотру.
Язык запросов Kusto был разработан для службы Azure Data Explorer, поэтому он оптимизирован для поиска в больших хранилищах данных в облачной среде. Названный в честь прославленного исследователя морских глубин Жака Кусто, он предназначен для погружения в океаны ваших данных и поиска скрытых в них сокровищ.
язык запросов Kusto также используется в Azure Monitor и поддерживает дополнительные функции Azure Monitor, которые позволяют получать, визуализировать, анализировать и анализировать данные в хранилищах данных Log Analytics. В Microsoft Sentinel вы используете инструменты на основе языка запросов Kusto каждый раз, когда визуализируете и анализируете данные либо ищете угрозы, независимо от того, применяете ли вы существующие правила и книги или создаете собственные.
Так как язык запросов Kusto является частью почти всего, что вы делаете в Microsoft Sentinel, четкое понимание того, как она работает, помогает вам получить это больше из siEM.
Что такое запрос
Запрос на языке запросов Kusto — это предназначенный только для чтения запрос, который обрабатывает данные и возвращает результаты — он не записывает никаких данных. Запросы работают с данными, организованными в иерархию баз данных, таблиц и столбцов, как в SQL.
Запросы формулируются на простом языке и используют модель потока данных, которая упрощает чтение, написание и автоматизацию синтаксиса.
Запросы на языке запросов Kusto состоят из операторов, разделенных точкой с запятой. Существует множество типов операторов, но только два широко используемых типа:
Операторы табличных выражений — это то, что мы обычно имеем в виду, когда говорим о запросах, то есть это само тело запроса. Об операторах табличных выражений важно знать то, что они принимают табличные входные данные (таблицу или другое табличное выражение) и создают табличные выходные данные. Необходим по крайней мере один из этих компонентов. Большая часть остальной части этой статьи обсуждает этот вид заявления.
Операторы let позволяют создавать и определять переменные и константы вне тела запроса, что упрощает чтение и повышает универсальность. Они являются необязательными и зависят от конкретных потребностей. Мы адресуем этот вид заявления в конце статьи.
Демонстрационная среда
Попрактиковаться в использовании операторов языка запросов Kusto, включая приведенные в этой статье, можно в демонстрационной среде Log Analytics на портале Azure. Эта среда практики не взимается, но для доступа к ней требуется учетная запись Azure.
Изучите демонстрационную среду. Как и Log Analytics в рабочей среде, его можно использовать различными способами:
Выбор таблицы, на основе которой будет создан запрос. На вкладке Таблицы, открывающейся по умолчанию (обведена красным прямоугольником в левом верхнем углу), выберите таблицу из списка таблиц, сгруппированных по разделам (слева ниже). Разверните раздел, чтобы просмотреть отдельные таблицы. Кроме того, можно развернуть каждую таблицу, чтобы просмотреть все ее поля (столбцы). Дважды щелкнув таблицу или имя поля, он помещает его в точку курсора в окне запроса. Введите остальную часть запроса после имени таблицы согласно указаниям ниже.
Поиск существующего запроса для изучения или изменения. Выберите вкладку Запросы (обведена красным прямоугольником в левом верхнем углу), чтобы просмотреть список доступных запросов. Можно также нажать кнопку Запросы на панели кнопок в правом верхнем углу. Вы можете изучить запросы, которые поставляются с Microsoft Sentinel. Дважды щелкнув запрос, в окне запроса помещает весь запрос в точку курсора.
Как и в этой демонстрационной среде, вы можете запрашивать и фильтровать данные на странице Журналы в Microsoft Sentinel. Можно выбрать таблицу и просмотреть ее столбцы. Можно изменить столбцы, отображаемые по умолчанию, с помощью средства выбора столбцов, а также задать диапазон времени по умолчанию для запросов. Если диапазон времени явно определен в запросе, фильтр времени недоступен (серый). Дополнительные сведения см. в разделе
Если вы подключены к единой платформе операций безопасности Майкрософт, вы также можете запрашивать и фильтровать данные на странице расширенной охоты в Microsoft Defender. Дополнительные сведения см. в статье "Расширенная охота с данными Microsoft Sentinel" на портале Microsoft Defender.
Структура запроса
Начать изучение языка запросов 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. Теперь давайте рассмотрим операторы, из которых строится запрос.
Типы данных
Прежде чем переходить к операторам запроса, давайте вкратце разберем типы данных. Как и в большинстве языков, тип данных определяет возможные действия и вычислительные операции со значением. Например, со значением типа string нельзя выполнять арифметические операции.
В языке запросов 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, timespan и guid, могут оказаться для вас новыми.
Dynamic имеет структуру, похожую на JSON, но с одним ключевым различием: она может хранить типы данных, зависящие от язык запросов Kusto, которые традиционные json не могут, например вложенное динамическое значение или интервал времени. Вот пример типа dynamic:
{
"countryOrRegion":"US",
"geoCoordinates": {
"longitude":-122.12094116210936,
"latitude":47.68050003051758
},
"state":"Washington",
"city":"Redmond"
}
timespan — это тип данных, который ссылается на меру времени, например часы, дни или секунды. Не путайте интервал времени с датой и временем, который оценивается до фактической даты и времени, а не меры времени. В таблице ниже приведен список суффиксов timespan.
Суффиксы timespan
Function | Description |
---|---|
D |
days |
H |
hours |
M |
minutes |
S |
с |
Ms |
мс |
Microsecond |
микросекунды |
Tick |
наносекунды |
Guid — это тип данных, представляющий 128-разрядный глобально уникальный идентификатор, который соответствует стандартному формату [8]-[4]-[4]-[4]-[4]-[12], где каждый [число] представляет число символов и каждый символ может варьироваться от 0 до 9 или f.
Примечание.
В языке запросов Kusto есть как табличные, так и скалярные операторы. Далее в этой статье под словом "оператор", скорее всего, будет иметься в виду табличный оператор, если не указано иное.
Получение, ограничение, сортировка и фильтрация данных
Основной словарь язык запросов Kusto — основа, которая позволяет выполнять большую часть задач — это коллекция операторов для фильтрации, сортировки и выбора данных. Остальные задачи требуют растяжения знаний о языке в соответствии с более сложными потребностями. Давайте немного рассмотрим некоторые команды, которые мы использовали в нашем предыдущем примере, и рассмотрим take
иsort
where
.
Для каждого из этих операторов мы рассмотрим его использование в предыдущем примере SigninLogs и узнайте, как полезные советы, так и рекомендации.
Получение данных
В первой строке любого базового запроса указывается, с какой таблицей необходимо работать. В случае Microsoft Sentinel это, скорее всего, имя типа журнала в рабочей области, например SigninLogs, SecurityAlert или CommonSecurityLog. Например:
SigninLogs
В язык запросов Kusto имена журналов чувствительны к регистру, поэтому SigninLogs
и signinLogs
интерпретируются по-разному. При выборе имен для пользовательских журналов, поэтому они легко идентифицируются и не слишком похожи на другой журнал.
Ограничение данных: take / limit
Оператор take (и идентичный оператор limit) используется для ограничения результатов: возвращается заданное число строк. За ним следует целое число, указывающее количество возвращаемых строк. Как правило, он используется в конце запроса после определения порядка сортировки, и в таком случае он возвращает заданное количество строк в верхней части сортировки.
Оператор take
может быть полезно использовать ранее в запросе в целях тестирования, чтобы не возвращать большие наборы данных. Однако если вы помещаете take
операцию перед любыми sort
операциями, возвращает строки, выбранные случайным образом, и, возможно, take
другой набор строк при каждом запуске запроса. Вот пример использования оператора take:
SigninLogs
| take 5
Совет
При работе с новым запросом, где может не быть известно, как выглядит запрос, можно поставить take
инструкцию в начале, чтобы искусственно ограничить набор данных для ускорения обработки и экспериментирования. Когда вы будете удовлетворены запросом, можно будет удалить начальный шаг take
.
Сортировка данных: sort / order
Оператор sort (и идентичный оператор order) служит для сортировки данных по указанному столбцу. В приведенном ниже примере результаты упорядочиваются по столбцу TimeGenerated в порядке убывания с помощью параметра desc, так что наибольшие значения будут находиться в начале. Для сортировку по возрастанию используется параметр asc.
Примечание.
По умолчанию сортировка выполняется по убыванию, поэтому порядок необходимо указывать, только если требуется отсортировать значения по возрастанию. Однако указание направления сортировки в любом случае делает запрос более читаемым.
SigninLogs
| sort by TimeGenerated desc
| take 5
Как упоминалось ранее, оператор sort
помещается перед оператором take
. Сначала необходимо выполнить сортировку, чтобы получить нужные пять записей.
Top
Оператор top позволяет объединить операции sort
и take
в один оператор:
SigninLogs
| top 5 by TimeGenerated desc
В случаях, когда две или более записей имеют одинаковое значение в столбце, по которым выполняется сортировка, можно добавить дополнительные столбцы для сортировки по. Дополнительные столбцы сортировки добавляются в виде списка с разделителями-запятыми после первого столбца сортировки, но перед ключевым словом порядка сортировки. Например:
SigninLogs
| sort by TimeGenerated, Identity desc
| take 5
Теперь, если TimeGenerated совпадает между несколькими записями, он пытается отсортировать по значению в столбце Identity .
Примечание.
Когда следует использовать sort
и take
, а когда — top
Если сортировка выполняется только по одному полю, используйте
top
, так при этом производительность будет выше, чем при использовании сочетанияsort
иtake
.Если вам нужно сортировать по нескольким полям (например, в последнем примере),
top
это не удается сделать, поэтому необходимо использоватьsort
иtake
.
Фильтрация данных: where
Оператор where , возможно, является наиболее важным оператором, так как это ключ, чтобы убедиться, что вы работаете только с подмножеством данных, которые относятся к вашему сценарию. Вы должны сделать все возможное, чтобы отфильтровать данные как можно раньше, так как это повышает производительность запросов, уменьшая объем данных, которые необходимо обработать в последующих шагах; он также гарантирует, что вы выполняете вычисления только для нужных данных. Дополнительные сведения см. в этом примере:
SigninLogs
| where TimeGenerated >= ago(7d)
| sort by TimeGenerated, Identity desc
| take 5
В операторе where
указываются переменная, оператор сравнения (скалярный) и значение. В данном случае мы использовали >=
, чтобы указать, что значение в столбце TimeGenerated должно быть больше (то есть позже) или равно семи дням назад.
В языке запросов Kusto есть два типа операторов сравнения: строковые и числовые. В таблице ниже приведен полный список числовых операторов.
Числовые операторы
Operator | Description |
---|---|
+ |
Дополнение |
- |
Вычитание |
* |
Умножение |
/ |
Подразделение |
% |
Остаток от деления |
< |
Меньше |
> |
Больше |
== |
Равно |
!= |
Не равно |
<= |
Меньше или равно |
>= |
Больше или равно |
in |
Соответствует одному из элементов |
!in |
Не равно ни одному из элементов |
Список строковых операторов — это более длинный список, так как он имеет перемутации для конфиденциальности регистра, расположений подстроки, префиксов, суффиксов и многое другое. Оператор ==
является как числовым, так и строковым, то есть его можно использовать как для чисел, так и для текста. Например, оба следующих оператора where будут допустимыми:
| where ResultType == 0
| where Category == 'SignInLogs'
Рекомендация. В большинстве случаев вы, вероятно, хотите отфильтровать данные по нескольким столбцам или отфильтровать один и тот же столбец несколькими способами. В таких случаях следует помнить две рекомендации.
Несколько операторов where
можно объединить в один шаг, используя ключевое слово and. Например:
SigninLogs
| where Resource == ResourceGroup
and TimeGenerated >= ago(7d)
При присоединении нескольких фильтров к одной инструкции с помощью ключевого слова вы получите более высокую производительность, поставив фильтры, которые ссылаются только на один where
столбец. Таким образом, лучше написать предыдущий запрос:
SigninLogs
| where TimeGenerated >= ago(7d)
and Resource == ResourceGroup
В этом примере первый фильтр ссылается на один столбец (TimeGenerated), а второй — на два столбца (Resource и ResourceGroup).
Получение сводных данных
Сводка является одним из самых важных табличных операторов в язык запросов Kusto, но это также один из более сложных операторов, чтобы узнать, если вы не знакомы с языками запросов в целом. Задача summarize
состоит в том, чтобы получить таблицу данных и вывести новую таблицу, агрегированную по одному или нескольким столбцам.
Структура оператора summarize
Базовая структура оператора summarize
выглядит так:
| summarize <aggregation> by <column>
Например, следующий запрос возвращает количество записей для каждого значения CounterName в таблице Perf:
Perf
| summarize count() by CounterName
Так как выходные данные summarize
являются новой таблицей, все столбцы, не указанные в summarize
инструкции, не передаются по конвейеру. Чтобы проиллюстрировать этот принцип, рассмотрим следующий пример:
Perf
| project ObjectName, CounterValue, CounterName
| summarize count() by CounterName
| sort by ObjectName asc
Во второй строке мы указываем, что мы заботимся только о столбцах ObjectName, CounterValue и CounterName. Затем мы производим агрегирование, чтобы получить число записей в столбце CounterName, и, наконец, пытаемся отсортировать данные в порядке возрастания по столбцу ObjectName. К сожалению, этот запрос завершается ошибкой (указывая, что имя объекта неизвестно), так как при сумме мы включили только столбцы Count и CounterName в новую таблицу. Чтобы избежать этой ошибки, мы можем добавить ObjectName в конец нашего summarize
шага, как показано ниже.
Perf
| project ObjectName, CounterValue , CounterName
| summarize count() by CounterName, ObjectName
| sort by ObjectName asc
Способ чтения summarize
строки в голове будет : "суммировать количество записей по CounterName и группировать по ObjectName". Вы можете продолжать добавлять столбцы, разделенные запятыми, в конец инструкции summarize
.
Продолжим предыдущий пример: если нам нужно агрегировать несколько столбцов одновременно, это можно сделать, добавив агрегаты в оператор summarize
через запятую. В приведенном ниже примере мы получаем не только количество всех записей, но и сумму значений в столбце CounterValue во всех записях (которые соответствуют любым фильтрам в запросе):
Perf
| project ObjectName, CounterValue , CounterName
| summarize count(), sum(CounterValue) by CounterName, ObjectName
| sort by ObjectName asc
Переименование агрегированных столбцов
Самое время поговорить об именах для агрегированных столбцов. В начале этого раздела мы сказали, что 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.
Оператор больше summarize
, чем мы можем охватить здесь, но вы должны инвестировать время, чтобы узнать его, потому что это ключевой компонент для любого анализа данных, который вы планируете выполнить на ваших данных Microsoft Sentinel.
Справка по агрегированию
Существует множество функций агрегирования, но чаще всего применяются sum()
, count()
и avg()
. Ниже приведен частичный список (полный список см. здесь).
Агрегатные функции
Function | Description |
---|---|
arg_max() |
Возвращает одно или несколько выражений, если аргумент достигает максимума |
arg_min() |
Возвращает одно или несколько выражений, если аргумент достигает минимума |
avg() |
Возвращает среднее значение в группе |
buildschema() |
Возвращает минимальную схему, которая допускает все значения динамических входных данных |
count() |
Возвращает число для группы |
countif() |
Возвращает число с предикатом для группы |
dcount() |
Возвращает приблизительное число уникальных элементов в группе |
make_bag() |
Возвращает контейнер свойств динамических значений в группе |
make_list() |
Возвращает список всех значений в группе |
make_set() |
Возвращает набор несовпадающих значений в группе |
max() |
Возвращает максимальное значение в группе |
min() |
Возвращает минимальное значение в группе |
percentiles() |
Возвращает приблизительное значение процентиля группы |
stdev() |
Возвращает стандартное отклонение в группе |
sum() |
Возвращает сумму элементов в группе |
take_any() |
Возвращает случайное значение nonempty для группы |
variance() |
Возвращает вариантность в группе. |
Выбор: добавление и удаление столбцов
При работе с запросами вы можете найти больше сведений, чем вам нужны в темах (т. е. слишком много столбцов в таблице). Или вам может потребоваться больше информации, чем у вас (т. е. необходимо добавить новый столбец, содержащий результаты анализа других столбцов). Рассмотрим несколько основных операторов для операций со столбцами.
Project и project-away
Оператор project примерно соответствует оператору select во многих других языках. Он позволяет выбирать столбцы, которые следует оставить. Порядок возвращаемых столбцов соответствует порядку столбцов, перечисленных в инструкции project
, как показано в этом примере:
Perf
| project ObjectName, CounterValue, CounterName
Как вы можете себе представить, при работе с широкими наборами данных у вас может быть много столбцов, которые нужно сохранить, и указать их все по имени потребуется много ввода. Для таких случаев есть оператор project-away, который позволяет указать, какие столбцы следует исключить, а не оставить:
Perf
| project-away MG, _ResourceId, Type
Совет
Может быть полезно использовать оператор project
в двух местах в запросе: в начале и еще раз в конце. Использование project
на раннем этапе запроса позволяет повысить производительность за счет удаления больших фрагментов данных, которые не нужно передавать по конвейеру. Повторное использование в конце позволяет избавиться от столбцов, которые могли быть созданы на предыдущих шагах и не требуются в выходных данных.
Расширить
Оператор extend служит для создания нового вычисляемого столбца. Это может быть полезно, если требуется выполнить вычисление на основе существующих столбцов и просмотреть выходные данные для каждой строки. Рассмотрим простой пример, в котором вычисляется новый столбец с именем Kbytes путем умножения значения МБ (в существующем столбце Quantity) на 1024.
Usage
| where QuantityUnit == 'MBytes'
| extend KBytes = Quantity * 1024
| project DataType, MBytes=Quantity, KBytes
В последней строке в операторе project
столбец Quantity переименовывается в Mbytes, чтобы можно было легко определить, какая единица измерения относится к каждому столбцу.
Стоит отметить, что extend
также работает с уже вычисленными столбцами. Например, можно добавить еще один столбец с именем Bytes, который вычисляется на основе Kbytes:
Usage
| where QuantityUnit == 'MBytes'
| extend KBytes = Quantity * 1024
| extend Bytes = KBytes * 1024
| project DataType, MBytes=Quantity, KBytes, Bytes
Соединение таблиц
Большая часть вашей работы в Microsoft Sentinel может выполняться с помощью одного типа журнала, но иногда возникают случаи, когда требуется сопоставить данные или выполнить поиск по другому набору данных. Как и большинство языков запросов, язык запросов Kusto предлагает несколько операторов для выполнения различных типов объединений. В этом разделе мы рассмотрим наиболее используемые операторы union
и join
.
Union
Оператор union просто принимает две или более таблиц и возвращает все строки. Например:
OfficeActivity
| union SecurityEvent
Этот запрос возвращает все строки из таблиц OfficeActivity и SecurityEvent. Union
предлагает несколько параметров, которые можно использовать для настройки объединения. Два наиболее полезных — withsource и kind:
OfficeActivity
| union withsource = SourceTable kind = inner SecurityEvent
Параметр withsource позволяет указать имя нового столбца, значение которого в заданной строке — имя таблицы, из которой пришла строка. В примере мы назвали столбец SourceTable и в зависимости от строки значением является OfficeActivity или SecurityEvent.
Еще был указан параметр kind, который имеет два значения: inner или outer. В примере мы указали внутренний элемент, то есть единственные столбцы, которые хранятся во время объединения, являются теми, которые существуют в обеих таблицах. Если бы мы указали outer (то есть значение по умолчанию), были бы возвращены все столбцы из обеих таблиц.
Присоединение
Присоединение работает так же union
, как и вместо объединения таблиц, чтобы создать новую таблицу, мы присоединяем строки , чтобы создать новую таблицу. Как и в большинстве языков баз данных, существует несколько типов объединений. Общий синтаксис для join
имеет следующий вид:
T1
| join kind = <join type>
(
T2
) on $left.<T1Column> == $right.<T2Column>
После оператора join
мы указываем kind, то есть нужный тип объединения, за которым следует открывающая круглая скобка. В скобках указывается таблица, к которой нужно присоединиться, и любые другие инструкции запроса в этой таблице, которую вы хотите добавить. После закрывающей скобки мы используем ключевое слово on, за которым следуют левый (ключевое слово $left.<имяСтолбца>) и правый ($right.<имяСтолбца>) столбцы, разделенные оператором ==. Вот пример кода внутреннего соединения:
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 делает синтаксис более явным и считается более правильным.
Для справки в таблице ниже приведен список доступных типов объединений.
Типы соединений
Тип соединения | Description |
---|---|
inner |
Возвращает одну строку для каждого сочетания совпадающих строк из обеих таблиц. |
innerunique |
Возвращает строки из левой таблицы с уникальными значениями в связанном поле, которые имеют соответствие в правой таблице. Это тип объединения по умолчанию, если не указан иной. |
leftsemi |
Возвращает из левой таблицы все записи, имеющие совпадение в правой таблице. Возвращаются только столбцы из левой таблицы. |
rightsemi |
Возвращает из правой таблицы все записи, имеющие совпадение в левой таблице. Возвращаются только столбцы из правой таблицы. |
leftanti /leftantisemi |
Возвращает из левой таблицы все записи, не имеющие совпадения в правой таблице. Возвращаются только столбцы из левой таблицы. |
rightanti /rightantisemi |
Возвращает из правой таблицы все записи, не имеющие совпадения в левой таблице. Возвращаются только столбцы из правой таблицы. |
leftouter |
Возвращает все записи из левой таблицы. Для записей, не соответствующих в правой таблице, значения ячеек имеют значение NULL. |
rightouter |
Возвращает все записи из правой таблицы. Для записей, не соответствующих левой таблице, значения ячеек имеют значение NULL. |
fullouter |
Возвращает все записи из левой и правой таблиц независимо от совпадения. Несовпаденные значения имеют значение NULL. |
Совет
Рекомендуется помещать самую маленькую таблицу слева. В некоторых случаях соблюдение этого правила может существенно повысить производительность в зависимости от типов выполняемых объединений и размера таблиц.
Вычислить
Вы можете вспомнить, что еще в первом примере мы видели оператор оценки на одной из строк. Оператор 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
Этот оператор позволяет вызывать доступные подключаемые модули (встроенные функции). Многие из этих подключаемых модулей предназначены для обработки и анализа данных, например autocluster, diffpatterns и sequence_detect. Они позволяют проводить расширенный анализ и выявлять статистические аномалии и выбросы.
Подключаемый модуль, используемый в примере, называется bag_unpack, и делает его простым для создания фрагмента динамических данных и преобразования его в столбцы. Помните, что динамические данные — это тип данных, похожий на JSON, как показано в этом примере:
{
"countryOrRegion":"US",
"geoCoordinates": {
"longitude":-122.12094116210936,
"latitude":47.68050003051758
},
"state":"Washington",
"city":"Redmond"
}
В этом случае нам нужно суммировать данные по городу, но city (город) содержится в качестве свойства в столбце LocationDetails. Чтобы использовать свойство city в запросе, необходимо сначала преобразовать его в столбец с помощью 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 и присвоили ему выходные данные функции timespan, которая возвращает значение типа datetime. Затем мы завершили оператор let точкой с запятой. Теперь у нас есть новая переменная с именем aWeekAgo, которую можно использовать в любом месте запроса.
Как уже упоминалось, можно использовать инструкцию let для выполнения всего запроса и дать результату имя. Так как результаты запросов, будучи табличными выражениями, могут использоваться в качестве входных данных других запросов, этот именованный результат можно рассматривать как таблицу для выполнения другого запроса к ней. Ниже предыдущий пример немного изменен:
let aWeekAgo = ago(7d);
let getSignins = SigninLogs
| where TimeGenerated >= aWeekAgo;
getSignins
В этом случае мы создали второй оператор let, заключающий весь запрос в новую переменную с именем getSignins. Как и раньше, мы завершаем этот оператор let точкой с запятой. Затем мы вызываем переменную в конечной строке, которая выполняет запрос. Обратите внимание, что во второй инструкции let мы смогли использовать переменную aWeekAgo. Это связано с тем, что она была указана в предыдущей строке. Если бы мы поменяли местами операторы 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.
Книга "Углубленное изучение KQL для Microsoft Sentinel"
Воспользуйтесь книгой по языку запросов Kusto прямо в Microsoft Sentinel: Углубленное изучение KQL для Microsoft Sentinel. Она содержит пошаговые инструкции и множество примеров ситуаций, которые, скорее всего, будут встречаться вам в ходе повседневной работы по обеспечению безопасности. В ней также приводится много готовых к использованию примеров правил аналитики, книг, правил поиска и других элементов с запросами Kusto. Запустите эту книгу на странице книг в Microsoft Sentinel.
Книга "Углубленное изучение KQL" — освойте все тонкости языка KQL — отличная запись в блоге, в которой показано, как использовать эту книгу.
Дополнительные ресурсы
Ознакомьтесь с этой коллекцией материалов для обучения и развития навыков, чтобы расширить и углубить свои познания о языке запросов Kusto.