✅KQL(Kusto Query Language) 개요 및 구성
KQL은 microsoft sentinel 등에서 대규모 로그 데이터를 효율적으로 검색, 분석, 시각화하는 데 사용되는 언어입니다.
Search, Where, Let, Extend, Order, Project, Summarize, Render, Union, Join, Extract, Parse 등의 다양한 연산자를 활용하여 복잡한 쿼리를 구성합니다.
- Search: 모든 열과 테이블에서 키워드 검색
- Where: 특정 조건에 따라 결과 필터링
- Let: 변수 또는 표현식 정의 및 재사용
- Extend: 새 열을 추가하여 값 계산
- Summarize: 데이터를 그룹화하고 집계(예: 개수, 평균)
- Render: 쿼리 결과를 차트나 그래프로 시각화
- Union: 여러 쿼리 또는 테이블의 결과를 병합
- Join: 공통 필드를 기반으로 두 데이터셋의 행 결합
KQL의 중요성과 구조
KQL의 중요성
- KQL(Kusto Query Language)은 Microsoft Sentinel이나 Azure Monitor와 같은 도구에서 방대한 데이터 세트를 빠르게 검색, 분석, 시각화하는 데 필수적이다.
- 읽기 쉽고 효율적으로 설계되어 복잡한 쿼리도 쉽게 작성하고 이해할 수 있다.
- 로그나 텔레메트리 데이터 작업 시 추세 파악, 문제 탐지, 사고 조사를 신속하게 수행하는 데 강력한 도구이다.
KQL의 데이터 흐름 모델
KQL의 구조는 데이터 흐름 모델을 강조하며, 각 연산자(operator)가 데이터를 처리하고 다음 단계로 전달한다.
KQL 쿼리의 논리적 단계
- 데이터 필터링 및 준비 단계가 있다.
- 분석 조건 적용 단계가 있다.
- 의사 결정을 위한 증거 준비 단계가 있다.
KQL 쿼리의 기본 형태
- 가독성 있고 모듈화된 구문을 사용하여 분석가는 복잡한 쿼리를 점진적으로 구축하고 대규모 데이터 세트에 걸쳐 인사이트를 자동화할 수 있다.
- 대부분의 KQL 쿼리는 테이블 표현식 문(tabular expression statements)으로, 테이블을 입력받아 테이블을 반환한다.
KQL 연산자 체이닝
- 연산자(Operators)를 사용하여 쿼리를 구축한다.
- 각 연산자는 테이블을 받아 필터링이나 요약 같은 작업을 수행한 후 결과를 다음 연산자로 전달한다.
- 파이프 기호(|)를 사용하여 연산자를 연결하며, 데이터는 단계별로 흐르면서 모양이 다듬어진다.
2. 핵심 KQL 연산자 (단일 테이블 작업)
KQL의 기본 연산자들은 데이터 검색, 필터링, 변수 정의, 필드 계산, 정렬, 결과 열 선택 등에 사용된다.
2.1. 검색 및 필터링 연산자
- Search 연산자
- 제공된 키워드나 구문을 모든 열과 테이블에서 빠르게 검색하는 기능이다.
- 데이터가 어떤 필드에 있는지 확실하지 않을 때 유용하며, 초기 조사 단계에서 여러 로그에 걸쳐 의심스러운 활동 징후를 찾는 데 특히 유용하다.
- 특정 필드를 지정하지 않고 광범위하게 검색하는 '넓은 그물'과 같다.
- Where 연산자
- 특정 조건에 따라 결과를 좁히는 필터링을 위한 핵심 연산자이다.
- 특정 이벤트 ID, 사용자 이름 또는 시간 범위 등 조건이 참인 행만 표시하도록 지정한다.
- 쿼리 초기에 사용하여 노이즈를 제거하고 중요한 것에 집중하는 데 사용된다.
- search와 비교했을 때, where는 특정 필드를 대상으로 하므로 더 효율적이다.
2.2. 변수 및 계산 연산자
Let 연산자
- 변수나 표현식을 한 번 정의하고 쿼리 전체에서 재사용할 수 있게 해주는 바로 가기 기능이다.
- 시간 범위, 계산된 값, 또는 하위 쿼리 등을 저장하는 데 사용되어 쿼리를 읽기 쉽고 유지 관리하기 쉽게 만든다.
Extend 연산자
- 데이터를 계산하여 새로운 열을 동적으로 추가할 수 있게 한다 (Excel의 사용자 지정 필드와 유사).
- 필드의 일부를 변환하거나 추출할 때 유용하며, 예를 들어 이메일 주소에서 도메인 이름을 추출하거나 파일 경로에서 디렉터리를 추출할 수 있다.
Extend 연산자 활용 예시
- 예시 쿼리는 보안 이벤트 테이블에서 지난 1시간 동안의 이벤트만 가져온다.
- 프로세스 이름 또는 프로세스 필드가 비어 있는 행을 필터링하여 데이터 세트를 정리한다.
- Extend를 사용하여 전체 프로세스 이름에서 프로세스 부분을 잘라내어 "StartDir"이라는 새 열을 생성한다.
- 이 변환은 전체 실행 파일 경로에서 디렉터리 경로를 분리하여 추가 그룹화나 조사에 유용하다.
- 이 쿼리는 필터링, 정리, 데이터 모양 변환을 보여주는 좋은 예이다.
2.3. 정렬 및 열 선택 연산자
- Order 연산자
- 하나 이상의 열을 기준으로 결과를 오름차순 또는 내림차순으로 정렬한다.
- 가장 최근 또는 가장 오래된 이벤트를 맨 위로 가져올 때 유용하다.
- 예시에서는 지난 1시간의 보안 이벤트 데이터를 가져와 프로세스 필드가 비어 있는 행을 필터링한 후, Extend로 "StartDir" 열을 생성한다.
- Order By를 사용하여 "StartDir"을 내림차순으로, 그다음 process를 오름차순으로 정렬한다.
- 이러한 정렬 방식은 유사한 디렉터리 경로를 그룹화하면서 프로세스 이름을 읽기 쉬운 순서로 유지하는 데 유용하다.
- Project 연산자 계열
- Project 연산자: 결과에 포함할 열을 선택하고 사용자 지정 뷰를 만들 때 사용되며, 열 이름 변경도 가능하다.
- Project-Reorder: 열의 순서를 변경하려면 원하는 순서대로 나열하면 된다.
- Project-Away 연산자: 포함할 모든 열을 나열하는 대신 제외할 몇 개의 열만 지정할 때 사용된다.
3. 쿼리 결과 분석 및 시각화
결과 분석을 위해 데이터를 그룹화하고 집계하며, 결과를 시각화하여 인사이트를 얻는다.
3.1. Summarize 연산자를 사용한 데이터 집계
- Summarize 연산자
- 데이터를 그룹화하고 개수(count), 평균(average), 최대/최소값 등을 계산할 때 사용되며, Excel의 피벗 테이블과 유사하다.
- 요약 후에는 Project 명령을 사용하여 열 이름을 바꾸거나 필요한 열만 표시하여 출력을 정리하는 것이 일반적이다.
- Summarize와 Project를 함께 사용하면 추세를 분석하고 깔끔하고 집중된 결과를 제시하는 강력한 방법을 제공한다.
- Summarize 활용 예시
- 첫 번째 쿼리: 지난 1시간 동안의 보안 이벤트에서 고유 IP 주소의 개수(DCount)를 요약하여 활동 급증을 확인하는 데 유용하다.
- 두 번째 쿼리 (Sign-in Log):
- 시간 범위(30일)와 임계값(1)을 변수로 정의한다.
- 지난 30일 동안 "invalid password" 결과가 있는 시도만 필터링한다.
- 사용자 및 IP 주소별로 고유 애플리케이션 액세스 횟수를 요약한다.
- 최소한 하나의 고유 애플리케이션이 있는 항목만 필터링하여 의심스러운 로그인 동작을 식별하는 데 도움을 줄 수 있다.
- arg_max 및 arg_min 함수 사용
- arg_max: 컴퓨터별로 가장 최신(latest) time generated 타임스탬프를 가진 행 전체를 반환한다.
- arg_min: 동일한 컴퓨터에 대해 가장 초기(earliest) time generated 타임스탬프를 가진 행 전체를 반환한다.
- 이 함수들은 특정 항목의 첫 번째 또는 마지막 발생과 관련된 전체 데이터 행을 가져오는 데 매우 유용하다.
- 두 쿼리 모두 결과를 컴퓨터별로 그룹화하여 시스템이 특정 이벤트를 처음 또는 마지막으로 보고한 시점을 추적하는 데 유용하다.
- make_list 및 make_set 비교
- 두 쿼리 모두 Event ID 4624 (성공적인 로그인)를 지난 1시간 동안 필터링한다.
- make_list: 각 컴퓨터와 연결된 모든 계정 이름을 반환하며, 중복을 포함한다 (여러 번 로그인한 사용자는 이름이 여러 번 나열됨).
- make_set: 각 컴퓨터별로 고유한 계정 이름만 반환하여 중복을 제거한다.
3.2. Render 연산자를 사용한 시각화
- Render 연산자
- 쿼리 결과를 차트나 그래프와 같은 시각화 형태로 반환하는 데 사용되며, 대시보드에 적합하다.
- 예시 1: 지난 1시간 이벤트를 필터링하고 계정별 이벤트 수를 계산한 후, Render bar chart로 표시하여 가장 활동적인 계정을 빠르게 확인할 수 있다.
- 시간 기반 시각화
- 예시 2: 지난 1시간 이벤트를 1분 간격으로 그룹화하고(bin Time-Generated one-minute), 각 시간대의 이벤트 수를 계산한다.
- Render time chart 명령을 사용하여 시간이 지남에 따라 활동이 어떻게 변하는지 보여준다.
- Render 연산자는 쿼리 결과를 시각적 차트로 변환하여 추세나 이상 징후를 한눈에 파악하기 쉽게 만든다.
4. 다중 테이블 쿼리 구축
여러 테이블의 데이터를 결합하여 더 포괄적인 분석을 수행한다.
4.1. Union 및 Join 연산자
- Union 연산자
- 두 개 이상의 쿼리 결과를 단일 데이터 세트로 결합하는 데 사용된다.
- 여러 테이블이나 쿼리의 출력을 하나의 큰 규칙 세트로 병합하는 것과 같다.
- 보안 이벤트 로그와 로그인 로그를 결합하여 활동에 대한 더 완전한 그림을 얻는 등 다른 소스의 데이터를 비교하거나 집계하는 데 유용하다.
- Join 연산자
- 공통 필드를 기반으로 두 데이터 세트의 행을 결합하며, Excel의 VLOOKUP이나 SQL의 Join과 유사하다.
- 예시 (로그온/로그오프 상관관계 분석):
- 왼쪽(로그온): Event ID 4624 (성공 로그인)를 필터링하고 계정별 로그인 수를 요약한다.
- 오른쪽(로그오프): Event ID 4634 (로그오프)를 필터링하고 계정별 로그오프 수를 요약한다.
- Join: 계정 필드를 기준으로 "Inner Join"을 사용하여 두 데이터 세트를 결합한다.
- 결과: 로그인 및 로그오프 이벤트가 모두 있는 계정만 표시되며, 세션 활동 식별이나 로그인 후 로그오프하지 않는 이상 징후 파악에 유용하다.
- Join 종류 (Join Flavors)
- Inner Join: 양쪽 테이블이 일치하는 행만 포함한다.
- Left Outer Join: 왼쪽 테이블의 모든 행과 오른쪽 테이블의 일치하는 행만 포함한다.
- Right Outer Join: 오른쪽 테이블의 모든 행과 왼쪽 테이블의 일치하는 행만 포함한다.
- Full Outer Join: 일치하지 않는 곳에 NULL을 포함하여 양쪽 테이블의 모든 행을 포함한다.
- Inner Unique: Inner와 유사하나 오른쪽 키가 고유하다고 가정한다.
- 적절한 Join 종류 선택은 데이터 보강, 필터링 또는 완전성 보장 등 목표에 따라 달라진다.
5. 문자열 데이터 처리 및 함수 사용
KQL을 사용하여 비정형 및 구조화된 문자열 필드에서 데이터를 추출하고 쿼리 재사용을 위한 함수를 생성한다.
5.1. 문자열 추출 연산자
- Extract 연산자
- 정규 표현식(regular expressions)을 사용하여 문자열에서 특정 텍스트 조각을 추출할 때 사용된다.
- 하나의 필드에 묶여 있는 로그 메시지에서 IP 주소, 사용자 이름, 파일 경로 등을 추출하는 데 매우 유용하다.
- 정규 표현식 패턴과 원하는 캡처 그룹을 지정하면 된다.
- 데이터가 깔끔하게 구조화되어 있지 않고 사용 가능한 부분으로 분해해야 할 때 특히 유용하다.
- Parse 연산자
- 스마트 템플릿과 같으며, 정의된 패턴을 사용하여 문자열을 별도 필드로 분할할 수 있게 한다.
- 일관된 형식을 따르지만 깔끔한 열에 없는 로그 메시지와 같이 반구조화된 데이터에 적합하다.
- 정규 표현식보다 가독성이 높으며, 작업할 텍스트 구조를 이미 알고 있을 때 더 직관적인 추출 방법이다.
5.2. 외부 데이터 통합 및 함수 생성
- External Data 연산자를 사용한 외부 데이터 통합
- 데이터 소스: Azure Blob 저장소에 있는 외부 파일("users.TXT")에서 사용자 ID 목록을 가져온다.
- 액세스: SAS 토큰이 포함된 보안 URL을 사용하여 파일에 액세스한다.
- 통합: external data 연산자가 파일을 읽고 'user ID'라는 단일 열을 가진 가상 테이블로 취급한다.
- 필터링: 기본 사용자 테이블을 외부 목록의 사용자 ID와 일치하는 행만 포함하도록 필터링한다.
- 사용 사례: 목록을 기본 데이터 저장소에 가져오지 않고도 감시 목록, VIP 사용자 목록 또는 알려진 위협 행위자와 같은 외부 목록과 내부 로그를 교차 참조할 때 유용하다.
- KQL 함수 생성 및 사용
- 함수를 사용하면 쿼리를 단순화하고 재사용할 수 있다.
- 예시에서는 보안 이벤트 테이블을 Event ID 4672로 필터링한다.
- 이 쿼리를 "Privileges"라는 함수 이름으로 저장할 수 있으며, 마지막 줄에서 이 함수를 참조하여 호출한다.
- 논리를 다시 작성하지 않고도 동일한 필터링된 결과를 반환한다.
- 이 방식은 쿼리를 현대화하고 가독성 및 유지 관리성을 향상시킨다.
6. KQL 실습 및 기본 쿼리 실행
실제 환경에서 KQL 연산자를 적용하여 데이터를 검색하고 필터링하는 과정을 보여준다.
6.1. 기본 쿼리 및 Search/Where 연산자 실습
- 기본 테이블 쿼리
- security event 테이블 자체를 실행하면 테이블 내의 모든 데이터를 가져온다.
- 단점은 데이터가 매우 많은 경우 너무 많은 결과가 나와 사용이 불가능해질 수 있으므로 가능한 한 필터링하는 것이 좋다.
- Search In 연산자
- search 연산자를 사용하여 security event 테이블과 "App"으로 시작하는 모든 테이블에서 특정 내용을 검색한다.
- 이 쿼리는 검색 작업을 수행하는 또 다른 예시이다.
- Where 연산자를 사용한 필터링
- 시간 필터링: 보안 이벤트 테이블에서 지난 7일 이내에 발생한 항목을 찾는다.
- 특정 Event ID 필터링: 지난 7일 동안의 이벤트 중 Event ID가 4624인 항목을 찾는다.
- 이 특정 조건으로는 실험 환경에서 결과가 나오지 않았으나, 실제 환경에서는 시간 범위나 Event ID를 조작하여 결과를 얻을 수 있다.
- 복합 필터링: 지난 7일 동안의 이벤트 중 계정 유형(account type)이 "user"인 항목을 찾는다.
- 시간 범위 제어: 쿼리 내에 시간 관련 명령이 있으면 상단의 시간 범위 설정이 쿼리에 영향을 미치지만, 쿼리에서 시간 관련 부분을 제거하면 상단 UI에서 시간 범위를 수동으로 조작하여 결과를 얻을 수 있다.
- where 연산자의 중요성: 필터링을 위한 가장 인기 있는 연산자 중 하나이며, 테이블의 모든 것을 가져오는 대신 특정 항목을 찾을 때 사용된다.
6.2. Let 연산자를 사용한 변수 활용 실습
- 기본 변수 정의
- Let 연산자는 변수 문과 같다.
- time offset 변수를 "one hour"로, discarded Event ID 변수를 4688로 정의한다.
- 변수를 사용하는 이점은 쿼리를 여러 번 실행할 때 한 곳만 변경하여 다른 데이터 포인트로 쉽게 재실행할 수 있다는 점이다.
- 변수를 사용한 시간 범위 필터링
- time generated가 time offset (1시간)의 두 배인 2시간 전부터 time offset인 1시간 전 사이인 이벤트를 찾는다.
- 즉, 1시간 전과 2시간 전 사이에 발생한 Event ID 4688을 찾는다.
- 변수와 비교 연산자 활용
- != (같지 않음) 연산자를 사용하여 discarded Event ID (4688)가 아닌 모든 Event ID를 보여준다.
- 복잡한 변수 정의 (집계 기반)
- let low activity accounts 변수를 정의하여 보안 이벤트 테이블을 요약한다.
- summarize를 사용하여 각 고유 계정별 이벤트 수를 계산한다.
- 필터링 조건으로 카운트가 1,000 미만인 계정(저활동 계정)을 정의한다.
- 최종 쿼리에서는 이 변수를 사용하여 SQL을 포함하는 계정을 필터링한다.
- 주의: KQL은 대소문자를 구분하므로 대소문자 구분에 유의해야 한다.
7. 고급 연산자 실습 (Extend, Order, Project, Summarize)
데이터 변환, 정렬, 열 선택, 그리고 집계의 다양한 방법을 실습한다.
7.1. Extend 및 Order By를 사용한 데이터 변환
- 데이터 제외 및 디렉터리 추출
- 지난 7일간의 보안 이벤트 테이블을 조회한다.
- process name 또는 process 필드가 비어 있는(empty) 행을 제외한다.
- Extend 연산자를 사용하여 전체 프로세스 이름에서 프로세스 부분을 제거하여 "Start directory"라는 새 열을 생성한다.
- 결과 정렬
- 결과를 시작 디렉터리(starting directory) 내림차순(Z to A)으로 정렬한다.
- 그다음 프로세스(process) 오름차순(A to Z)으로 정렬한다.
- 시간 필드를 제거하고 사용자 지정 기간(몇 달 전)으로 설정하여 결과를 생성한다.
- 결과는 시작 디렉터리 내림차순, 프로세스 오름차순으로 정렬되며, 빈 필드는 제외된다.
7.2. Project 및 Summarize 연산자 활용
- Project 연산자를 사용한 열 선택
- 이전 쿼리 구문을 유지하고 Project 연산자를 추가한다.
- Project는 "보여줘(show me)"와 같으며, 결과에서 프로세스 및 시작 디렉터리 열만 표시하도록 제한한다.
- 이전 쿼리가 여러 열을 반환한 것과 달리, Project는 지정된 열만 보여준다.
- Project-Away 연산자
- Project 대신 Project Away를 사용하고 타임스탬프를 제거한 후 데이터를 생성한다.
- Project Away는 프로세스 이름 열을 제외한 모든 열을 보여준다.
- Project: 보여줄 것을 지정한다.
- Project Away: 제외할 것을 지정한다.
- Summarize by Count
- Summarize operator는 요약을 생성하는 데 사용된다.
- Event ID 4688에 대해 지난 7일간의 보안 이벤트 테이블을 조회한다.
- summarize by count: 필터링된 데이터를 프로세스 및 컴퓨터별로 그룹화하여 각 그룹의 인스턴스 수를 집계한다.
- 개별 인스턴스를 보는 대신, 각 프로세스 및 컴퓨터 조합에 대한 총 인스턴스 수만 요약하여 보여준다. (만약 summarize count를 사용하지 않았다면 수많은 행이 나왔을 것이다.)
- Summarize by DCount (Distinct Count)
- 이번에는 Summarize를 count 없이 사용한다.
- 필터링된 데이터를 D-count (Distinct Count)를 계산하여 집계한다.
- 이 쿼리는 지난 7일 동안 고유한 IP 주소가 몇 개 있었는지 보여준다.
- D-count는 해당 명령의 고유 인스턴스를 의미한다.
8. Summarize의 고급 함수 (arg_max/min, make_list/set)
Summarize 연산자를 사용하여 특정 시점의 레코드나 목록을 추출하는 방법을 배운다.
8.1. arg_max 및 arg_min 함수
- arg_max/arg_min 사용 목적
- 특정 컴퓨터(computer 필드 일치)를 필터링한다.
- Summarize arg-max: 컴퓨터별로 가장 최신(latest) time generated 타임스탬프를 가진 행 전체를 반환한다.
- 컴퓨터 필드를 지정하지 않으면 *를 사용하여 모든 항목에 대해 수행할 수 있다.
- 결과는 지정된 컴퓨터에 대한 가장 최신 타임스탬프를 보여준다.
- argmin 함수
- argmin: 가장 오래된(oldest) time generated를 가진 항목을 반환한다.
- 이 연산자는 모든 인스턴스를 보여주는 대신 해당 열의 가장 오래되거나 가장 새로운 항목만 보여준다.
8.2. Make List 및 Make Set 비교
- Summarize Make List
- 보안 이벤트 테이블에서 시간 범위와 Event ID 4624 (성공 로그인)를 필터링한다.
- 필터링된 이벤트를 컴퓨터별로 그룹화한다.
- Summarize Make List를 사용하여 각 컴퓨터에 대해 로그인한 사용자 계정 목록을 생성한다.
- Make_List는 모든 중복을 포함하여 계정 이름을 목록으로 집계한다.
- 결과는 컴퓨터별로 성공적으로 로그인한 사용자 목록을 보여준다.
- Make List와 Make Set의 차이점
- Make Set: 컴퓨터에 로그인한 계정의 고유한(distinct) 집합을 생성한다.
- Make List: 중복을 포함하여 모든 인스턴스를 포함한다.
- Make Set은 각 컴퓨터당 계정이 한 번만 나타나도록 보장한다.
9. 출력 형식 지정 및 다중 테이블 결합 (재검토)
결과를 시각화하고 여러 테이블을 결합하는 방법을 재확인한다.
9.1. Render 연산자를 사용한 시각화 재확인
- Render 연산자
- 쿼리 출력을 차트나 이미지 형태로 생성한다.
- Bar Chart 예시: 보안 이벤트 테이블에서 계정 필드별로 요약 개수를 계산한 후, 결과를 막대 차트로 표시한다.
- 주의: KQL에서 하이라이트된 부분만 실행되므로, 전체 쿼리를 실행하려면 하이라이트를 해제해야 한다.
- Time Chart 예시
- Render time chart 옵션을 사용하여 시간 경과에 따른 변화를 시각화한다.
9.2. Union 연산자 재검토
- Union 연산자
- 두 개 이상의 테이블에서 데이터를 검색하거나 병합할 수 있게 한다 (이전에는 단일 테이블만 사용).
- 예시에서는 security event 테이블과 sign-in logs 테이블의 모든 데이터를 가져와 결합한다.
- 출력은 두 테이블의 모든 열을 포함한다.
- Union 변형 및 Summarize 결합
- Union, any table that starts with app: "app"으로 시작하는 모든 테이블의 데이터를 가져온다.
- summarize by count by type field: type field별로 개수를 요약하여, app events 및 app page views 등 각 앱 관련 테이블의 총 항목 수를 보여준다.
9.3. Print 연산자
- Print 연산자: 로그에서 데이터를 추출하고 Print 연산자를 사용하여 출력할 수 있다.
- 특정 값(예: 45.6)을 추출하여 그 결과를 출력으로 확인할 수 있다.
뒷공부 2일차.. 눈이 아파서 죽어버릴거만 같다
지난 주까지 땃어야하는데 후ㅜㅜㅜㅜㅜㅜㅜㅜㅜ
'Azure' 카테고리의 다른 글
| [SC-200]Microsoft 365 Defender (0) | 2026.03.09 |
|---|---|
| [SC-200]Microsoft Sentinel (0) | 2026.03.08 |
| Azure 권한 체계 (RBAC · Entra ID 역할 · PIM · Wiz 연계) (0) | 2026.03.04 |
| [SC-200] DLP - Data Loss Prevention (0) | 2026.03.02 |
| [SC-200]Microsoft Purview (0) | 2026.03.02 |