본문으로 건너뛰기

© 2026 Molayo

Dev.to헤드라인2026. 05. 31. 19:48

AI에게 보고서 작성을 맡기되, 경보(Alert) 결정은 맡기지 마라

요약

보안 분석 도구 TriageLens 구축 사례를 통해 AI의 결정론적 한계를 극복하는 아키텍처를 제안합니다. 탐지와 점수 산정은 결정론적인 TypeScript 코드로 처리하고, AI는 분석 보고서 작성에만 활용하여 신뢰성을 확보하는 '분리(the split)' 전략을 설명합니다.

핵심 포인트

  • AI는 탐지 결정이 아닌 보고서 작성 도구로만 활용해야 함
  • 탐지, 파싱, 위험 점수 산정은 결정론적인 코드로 구현
  • 규칙을 순수 함수로 작성하여 유닛 테스트와 감사 가능성 확보
  • 모델 교체 시에도 탐지 결과의 일관성 유지

저는 TriageLens라는 SOC 분류(triage) 도구를 구축해 왔는데, 이 모든 것은 하나의 짜증스러운 점으로부터 시작되었습니다. 제가 시도해 본 모든 "AI 보안 분석가" 데모는 그저 프롬프트에 로그를 붙여넣은 챗봇에 불과했습니다. 두 번 물어보면 두 개의 서로 다른 판결이 나옵니다. 분류(triage) 작업에서 이는 쓸모가 없습니다. 도구가 한 번은 "무차별 대입 공격(brute force), 심각(critical)"이라고 했다가, 다음번에는 "괜찮아 보임"이라고 한다면, 저는 그 어떤 답변도 신뢰할 수 없습니다.

그래서 저는 초기에 명확한 선을 그었습니다. AI는 무엇이 탐지 사항(finding)인지 결정할 권한이 없습니다. AI는 단지 그 탐지 사항을 작성할 뿐입니다.

분리 (the split)

파싱(Parsing), 탐지(detection), 그리고 위험 점수 산정(risk scoring)은 순수 TypeScript로 이루어집니다. 모델은 관여하지 않습니다. 파이프라인은 Windows Security 4688, Sysmon Event 1, Linux SSH auth.log, 그리고 일반적인 JSON을 하나의 이벤트 형태로 정규화(normalize)하고, 해당 이벤트들에 대해 탐지 규칙(detection rules) 목록을 실행하며, 결과에 0-100 사이의 점수를 매깁니다. 모든 과정은 결정론적(deterministic)입니다. 동일한 로그가 입력되면 매번 동일한 탐지 사항이 출력됩니다.

AI 레이어는 맨 마지막에 위치합니다. 이미 존재하는 구조화된 탐지 사항(findings)을 가져와서 분석가 스타일의 산문으로 변환합니다: 요약, 탐지 사항별 노트, 우선순위가 지정된 다음 단계 등이 그것입니다. 만약 제공자(provider)를 내장된 데모용 모델에서 Ollama에서 Claude로 교체하더라도, 탐지 사항과 MITRE 매핑은 전혀 변하지 않습니다. 오직 문구만 바뀔 뿐입니다.

이러한 특성이야말로 제가 실제로 중요하게 생각하는 부분입니다. 탐지(detections)는 감사(auditable) 가능합니다. 모델은 단지 작성자일 뿐입니다.

규칙은 단지 함수일 뿐이다 (a rule is just a function)

각 탐지 규칙은 이벤트를 살펴보고 증거 문자열(evidence strings)을 반환하는 순수 함수(pure function)입니다. 빈 배열은 규칙이 실행되지 않았음을 의미합니다. 제가 가장 만족스러워하는 규칙인 체이닝(chained) 규칙은 다음과 같습니다:

{
  id: 'successful-auth-after-brute-force',
  title: 'Successful login after brute-force activity',
...

그 자체만 놓고 보면, 실패한 SSH 로그인 로그 더미는 그저 노이즈일 뿐입니다. 수많은 호스트가 하루 종일 무차별 대입 공격(spraying)을 받습니다. 상황을 바꾸는 것은 방금 여러 번 실패했던 것과 동일한 IP로부터 발생한 성공 사례입니다. 무차별 대입(brute-force) 규칙 단독으로는 high 등급입니다. 하지만 '무차별 대입 후 성공(success-after-brute-force)' 규칙은 T1110 및 T1078에 매핑되어 critical 등급이 됩니다. 왜냐하면 그 시점에는 단순한 배경 스캐닝이 아니라 실제 침해(compromise) 상황을 보고 있을 가능성이 높기 때문입니다.

이를 일반 함수(plain function)로 작성한다는 것은 몇 가지 가짜 이벤트(fake events)를 사용하여 유닛 테스트(unit test)를 수행할 수 있고, 제가 원하는 정확한 케이스에서 작동하는지 확인할 수 있음을 의미합니다. 프롬프트 튜닝(prompt tuning)이나 "JSON으로 응답해 주세요" 같은 요청도 필요 없습니다. countFailuresByIp는 약 6줄 정도로 구성되며, 소스 IP당 auth-failure 이벤트를 카운트합니다. 전체 규칙 파일은 체크리스트처럼 위에서 아래로 읽힙니다.

처음에 시도했다가 포기한 것

제 첫 번째 버전은 실제로 모델에 원시 로그(raw logs)를 전달하고 결과물을 JSON으로 반환하도록 요청하는 방식이었습니다. 데모에서는 작동했지만, 조금이라도 이상한 데이터를 입력하는 순간 무너졌습니다. 때로는 로그에 없는 이벤트 ID를 스스로 만들어내기도 했습니다. 한 번은 정상적인 svchost를 LOLBin(Living Off the Land Binary)으로 확신하며 플래그를 지정하기도 했습니다. 또한 JSON 결과물 끝에 주석이나 마크다운 펜스(markdown fence)가 붙어 파서(parser)를 망가뜨리는 경우도 종종 발생했습니다.

저는 프롬프트(prompt)를 수정하며 이 문제를 해결하려고 하루를 보냈지만, 결국 그 접근 방식을 완전히 포기했습니다. 탐지(detection) 로직을 코드로 옮긴 것은 성능 때문이 아니라, "이것을 신뢰할 수 있어야 한다"는 결정 때문이었습니다. 모델은 요약(summary)을 작성하는 데는 뛰어나지만, 진실의 근원(source of truth)이 되는 데는 서툽니다.

아직 미흡한 점

솔직하게 말씀드리겠습니다. 이 도구는 EVTX를 이미 JSON으로 내보낸 경우에만 읽을 수 있습니다. 아직 네이티브 .evtx 바이너리 파서(binary parser)가 없으며, 이는 로드맵의 다음 단계에 있습니다. 현재로서는 내보내기 단계가 번거로운 수동 작업입니다. 규칙 세트가 7개로 적기 때문에 명백한 것들(인코딩된 PowerShell, Office가 자식 프로세스를 생성하는 경우, 로그 삭제, SSH 체인 등)은 잡아내지만, 많은 것들을 놓칩니다. 저만 제 방식대로 탐지 로직을 작성하지 않도록 Sigma 임포트(import) 기능을 추가하고 싶습니다.

또한 이것은 SIEM(Security Information and Event Management)이 아니며, 그런 척하지도 않습니다. 이것은 학습용 프로젝트이자 분류(triage) 보조 도구입니다. 튜닝된 탐지 콘텐츠나 무엇이 중요한지 결정하는 인간을 대체하지는 않습니다.

하지만 설정 없이 바로 실행할 수 있습니다. npm install, npm run dev를 입력하면 샘플 로그가 이미 로드되어 있으며, 'Analyze'를 클릭하기만 하면 됩니다. 기본 제공자(default provider)는 API 키가 필요하지 않으므로, 아무것도 가입하지 않고도 전체 루프를 확인할 수 있습니다.

직접 살펴보거나 어떤 규칙이 잘못되었는지 알려주고 싶다면 저장소(Repo)는 여기 있습니다: https://github.com/TiltedLunar123/triagelens

React, TypeScript, Vite, 그리고 규칙 테스트를 위한 vitest로 구축되었습니다. 탐지(detection) 아이디어를 환영합니다. 그 부분이 제가 가장 성장하고 싶은 부분입니다.

AI 자동 생성 콘텐츠

본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.

원문 바로가기
0

댓글

0