앞선 글에서는 Prometheus에서 이상 구간을 찾고, Tempo의 Trace로 요청 경로를 좁힌 뒤, Loki에서 같은 TraceId의 상세 근거를 확인했다. 그러나 흔적을 찾았다고 사고 대응이 끝나는 것은 아니다. 조사 결과를 보존하고, 영향 범위를 정하고, 위험한 경로를 차단하고, 서비스를 안전하게 복구해야 한다.
이번 글에서는 “입력 Guardrail이 차단을 기록했는데 그 뒤에 Tool 호출이 실행됐다”는 가상 사건을 처음부터 끝까지 처리한다. 목표는 공격 문장을 다시 실행하는 것이 아니다. 이미 수집된 Log·Trace·Metric과 배포 정보를 이용해 사건을 재구성하고, 재현 가능한 증거 묶음과 대응 기록을 만드는 방어 실습이다.

사고 조사는 로그 검색보다 큰 일이다
NIST SP 800-61 Rev.3은 사고 대응을 별도의 일회성 작업이 아니라 조직의 사이버보안 위험관리 활동 전체에 통합하도록 설명한다. 준비가 되어 있어야 탐지·대응·복구가 빨라지고, 복구에서 얻은 교훈이 다시 보호와 탐지 능력을 개선한다.
LLM 서비스에서는 이 순환이 특히 중요하다. 모델 호출만 조사해서는 부족하다. 사용자 인증, Tenant 경계, 시스템 프롬프트 버전, RAG 검색 결과, Guardrail 결정, Tool 권한, 다운스트림 변경과 출력 처리까지 하나의 사건으로 묶어야 한다.
준비 → 탐지·분석 → 범위 확정 → 증거 보존
↑ ↓
개선 ← 사후 분석 ← 복구·검증 ← 차단·근절
먼저 역할과 결정권을 정한다
관측 도구는 사실을 보여 주지만 대응 결정을 대신하지 않는다. Incident Commander는 우선순위와 의사결정을 조정하고, 조사 담당자는 증거와 타임라인을 만든다. 서비스 담당자는 기능 제한과 복구를 수행하고, IAM 담당자는 계정·Token·권한을 점검한다. 개인정보나 규제 대상 데이터가 관련되면 법무·개인정보 담당자가 보존 기간과 통지 요건을 판단한다.
| 역할 | 주된 책임 | 단독으로 하면 안 되는 일 |
| Incident Commander | 심각도, 범위, 의사결정 기록 | 근거 없이 침해 확정 |
| 조사 담당자 | Timeline, Query, 증거 Hash | 원본 증거 직접 수정 |
| 서비스·플랫폼 담당자 | 기능 제한, 배포, 복구 | 증거 확보 전 로그 삭제 |
| IAM·데이터 담당자 | Token 폐기, 권한·Tenant 확인 | LLM에게 최종 인가 판단 위임 |
| 개인정보·법무 담당자 | 보존·통지 요건 판단 | 모든 Telemetry 무기한 보관 |
초기 심각도는 “공격 문자열이 보였는가”보다 실제 영향으로 정한다. 차단된 입력만 있다면 탐지 이벤트일 수 있다. 차단 후 Tool 실행, 다른 Tenant 데이터 조회, Secret 노출 또는 외부 시스템 변경이 확인되면 우선순위를 높여야 한다.
실습 사건: 차단 뒤에도 Tool이 실행됐다
다음은 실습용 가정이다.
- input_guard Span은 security.decision=block을 기록했다.
- 같은 Trace 안에 tool_policy와 tool_call Span이 이어진다.
- Tool 결과가 실제 외부 변경을 만들었는지는 아직 모른다.
- 원본 프롬프트와 응답은 관측 저장소에 저장하지 않았다.
이 상태에서 “Guardrail 우회로 데이터가 유출됐다”고 단정하면 안 된다. 먼저 세 가지 가설을 분리한다.
- 계측 오류: Span 순서나 속성만 잘못 기록됐다.
- 제어 흐름 결함: 차단 결정 뒤 실행 중단이 누락됐다.
- 권한 침해: Tool이 실제로 허용되지 않은 읽기·쓰기를 수행했다.
조사의 목적은 가장 자극적인 설명을 고르는 것이 아니라 증거로 가설을 하나씩 제거하는 것이다.
1단계: 사건 번호와 조사 시간 범위를 고정한다
먼저 사건 번호, 조사자, 기준 시간대와 최초 Alert를 기록한다. 화면에 보이는 상대 시간인 “10분 전” 대신 UTC 절대시각을 사용한다.
incident_id: INC-LLM-2026-0007
detected_at: 2026-09-03T01:10:00Z
window_start: 2026-09-03T00:55:00Z
window_end: 2026-09-03T01:25:00Z
initial_signal: block 이후 tool_call Span 발견
Prometheus에서 사건 전후의 차단률과 Tool 호출량을 확인한다.
sum(rate(llm_security_requests_total{decision="block"}[5m]))
/
sum(rate(llm_security_requests_total[5m]))
sum by (tool, result) (
increase(llm_tool_calls_total[30m])
)
Metric은 이상 시점과 규모를 알려 주지만 개별 TraceId를 Label로 넣지 않는다. 요청마다 다른 값을 Label로 사용하면 시계열 수가 폭증하기 때문이다.
2단계: Trace에서 실행 경계를 확인한다
Tempo에서 차단된 입력 Trace를 찾는다. 속성 이름은 이 실습의 예이며 실제 계측 스키마에 맞춰야 한다.
{ name = "input_guard" && span.security.decision = "block" }
Trace를 열어 다음 순서를 확인한다.
request
├─ authenticate
├─ input_guard decision=block
├─ tool_policy ← 존재하면 제어 흐름 의심
└─ tool_call ← 실제 실행·결과 확인 필요
Span 시작·종료 시각, Status, service.name, 배포 버전, Tool 이름과 인가 결정 ID를 기록한다. 프롬프트 원문, Access Token, Tool의 민감한 인자를 증거 메모에 복사하지 않는다. 필요한 경우 값 대신 존재 여부, 길이, 분류와 별도 보관 위치를 남긴다.
3단계: Loki와 다운스트림 감사 로그로 실제 영향을 검증한다
같은 TraceId로 애플리케이션 Log를 찾는다. TraceId는 Loki의 고카디널리티 인덱스 Label보다 구조화 메타데이터나 로그 필드에 두는 편이 적절하다.
{service_name="llm-security-app"}
| trace_id="조사할_TraceId"
애플리케이션 Log만으로 Tool 실행 성공을 확정하지 않는다. 대상 API, 데이터베이스, 클라우드 감사 로그에서 같은 request_id, 작업 ID 또는 인가 결정 ID를 교차 확인한다.
| 확인 지점 | 남겨야 할 최소 증거 | 답할 질문 |
| Application | TraceId, RequestId, 배포 버전 | 차단 뒤 코드 경로가 계속됐나? |
| Guardrail | 규칙 ID, 결정, 정책 버전 | 어떤 정책이 무엇을 결정했나? |
| IAM·Policy Engine | 주체, Tenant, 결정 ID | 누가 어떤 권한으로 승인됐나? |
| Tool Gateway | Tool 이름, 결과 코드, 대상 분류 | 호출이 실제 전송됐나? |
| Downstream | 감사 이벤트 ID, 변경 전후 상태 | 읽기·쓰기가 실제 반영됐나? |
| RAG | 문서 ID·버전, Tenant Filter 결과 | 다른 Tenant 자료가 포함됐나? |
LLM이나 Guardrail의 판단은 업무 인가 증거가 아니다. 최종 접근 권한은 신뢰할 수 있는 Application/API 계층과 다운스트림 시스템이 강제하고, 조사자는 그 인가 기록을 확인해야 한다.
4단계: 원본을 건드리지 않는 증거 묶음을 만든다
조사 결과는 나중에 같은 결론을 재검토할 수 있어야 한다. 원본 저장소를 수정하지 않고 Query, 조회 시간 범위, 내보낸 결과, 설정·배포 버전과 Hash를 별도 사건 디렉터리에 보존한다.
mkdir -p incident-INC-LLM-2026-0007/{queries,exports,config,notes}
cp prometheus-query.txt incident-INC-LLM-2026-0007/queries/
cp trace-summary.json incident-INC-LLM-2026-0007/exports/
cp redacted-events.jsonl incident-INC-LLM-2026-0007/exports/
cp deployment-version.txt incident-INC-LLM-2026-0007/config/
find incident-INC-LLM-2026-0007 -type f ! -name SHA256SUMS \
-print0 | sort -z | xargs -0 sha256sum \
> incident-INC-LLM-2026-0007/SHA256SUMS
tar --sort=name --mtime='UTC 1970-01-01' \
--owner=0 --group=0 --numeric-owner \
-czf INC-LLM-2026-0007-evidence.tar.gz \
incident-INC-LLM-2026-0007
sha256sum INC-LLM-2026-0007-evidence.tar.gz
macOS에서는 GNU sha256sum 대신 shasum -a 256을 사용할 수 있다. tar 옵션도 구현마다 다르므로 운영 환경에서 지원 여부를 확인한다.
notes/timeline.md에는 관찰 사실과 해석을 분리한다.
[사실] 01:03:14Z input_guard가 block 기록
[사실] 01:03:14Z 같은 Trace에 tool_call Span 존재
[사실] Downstream 감사 로그에 write 이벤트 없음
[해석] 호출 준비 단계까지 진행됐으나 외부 변경 증거는 확인되지 않음
[미확인] 응답이 사용자에게 반환됐는지 Gateway 로그 확인 필요
Hash는 파일이 변했는지 확인하는 수단이지 수집 과정의 신뢰성을 자동으로 보장하지 않는다. 수집자, 수집 시각, 원본 위치, 변환·마스킹 과정과 접근 기록도 함께 남긴다.
Telemetry 자체가 새로운 유출 경로가 되지 않게 한다
OpenTelemetry 공식 보안 지침은 Telemetry가 PII, 애플리케이션 데이터와 네트워크 패턴을 의도치 않게 포함할 수 있다고 경고한다. 무엇이 민감한지는 OpenTelemetry가 자동으로 판단하지 못하므로 구현자가 최소수집·삭제·Hash·Redaction 정책을 정해야 한다.
기본 증거에는 다음을 우선한다.
- 원문 대신 TraceId, 규칙 ID, 결정과 처리 단계
- 시스템 프롬프트 내용 대신 승인된 버전 또는 Hash
- RAG 원문 대신 문서 ID·버전과 Tenant Filter 결과
- Tool 인자 원문 대신 작업 종류, 대상 분류와 결과 코드
- 사용자 식별자 대신 사건 조사에 필요한 제한된 가명 식별자
원문이 정말 필요하면 일반 관측 저장소와 분리된 암호화 저장소, 제한된 역할, 짧은 보존 기간과 열람 감사가 필요하다. 법률·계약·규제 요건은 조직별 담당자가 판단해야 한다.
5단계: 증거를 확보한 뒤 피해를 제한한다
Containment, 즉 피해 제한은 “서버를 모두 끈다”와 같은 한 가지 행동이 아니다. 영향과 업무 연속성에 맞춰 가장 좁은 안전 조치부터 적용한다.
| 확인된 상황 | 우선 조치 예 | 확인할 부작용 |
| 차단 뒤 특정 Tool만 호출 | 해당 Tool Route 비활성화 | 핵심 업무 중단 여부 |
| 특정 정책 버전의 결함 | 이전 승인 버전으로 Rollback | 새 정책에서 막던 위험 재노출 |
| Token 노출 가능성 | Token 폐기·회전, Session 종료 | 자동화·배치 작업 실패 |
| Tenant Filter 누락 | Retrieval API Fail-closed | 검색 기능 전체 가용성 |
| 외부 쓰기 확인 | 쓰기 권한 제거, 대상 상태 복구 | 증거와 정상 변경의 구분 |
가능하면 LLM 전체를 내리기보다 Tool 쓰기를 읽기 전용으로 바꾸거나, 위험 Route를 차단하고, 사람의 승인을 요구하는 방식으로 기능을 축소한다. 다만 Secret 유출이나 광범위한 Tenant 경계 붕괴가 의심되면 더 강한 격리가 필요하다.
모든 긴급 변경에는 실행자, 승인자, 시각, 변경 내용, Rollback 조건과 관련 사건 번호를 기록한다.
6단계: 근절과 복구를 검증한다
원인이 block 반환 뒤 함수가 종료되지 않는 제어 흐름 결함이었다면 단순 정책 추가로 끝내지 않는다. 코드에서 Fail-closed를 강제하고 자동 테스트를 추가한다.
decision = input_guard.check(request)
if decision.blocked:
audit(decision)
return blocked_response()
# 이 줄 아래는 허용된 요청만 도달해야 한다.
result = tool_gateway.execute(authorized_request)
복구 전에는 최소 네 가지를 시험한다.
- 차단 요청에서 tool_policy와 tool_call Span이 생성되지 않는다.
- 허용 요청은 정상 동작하고 다운스트림 인가를 다시 통과한다.
- 다른 Tenant의 RAG 문서와 Tool 대상에 접근할 수 없다.
- Metric·Trace·Log와 Alert가 새 버전에서도 연결된다.
처음에는 Canary나 제한된 Tenant에 배포하고, 오류율·차단률·Tool 호출량을 관찰한다. “패치 배포 성공”은 복구 완료가 아니다. 정상 기능, 보안 경계와 관측 기능이 함께 회복됐을 때 종료한다.
7단계: 사후 분석을 탐지와 설계 개선으로 되돌린다
사후 보고서는 사람을 탓하는 문서가 아니라 다음 사건의 탐지·대응 시간을 줄이는 입력이다.
- 최초 신호와 실제 영향은 무엇이었나?
- 어떤 증거가 결론을 바꿨나?
- 발견과 차단에 각각 얼마나 걸렸나?
- Telemetry에서 부족하거나 과도했던 필드는 무엇인가?
- Runbook, Test, Alert와 권한 경계를 무엇으로 바꿀 것인가?
이번 사건에서는 다음 개선이 자연스럽다.
예방: block 이후 즉시 종료하는 공통 Middleware
탐지: blocked Trace에 tool_call이 존재하면 Critical Alert
제한: Tool Gateway에서 사용자·Tenant 권한을 다시 검사
검증: CI에 차단 후 Side Effect 부재 테스트 추가
운영: 사건 Query와 증거 Manifest 템플릿 표준화
실습 완료 체크리스트
- 사건 번호, 담당자, UTC 시간 범위와 초기 가설을 기록했다.
- Metric으로 이상 시점과 규모를 확인했다.
- Trace에서 차단 뒤 실행 경로를 확인했다.
- Application과 Downstream 감사 로그를 교차 검증했다.
- 관찰 사실, 해석과 미확인 항목을 분리했다.
- 원본을 수정하지 않고 Export와 Query를 보존했다.
- 파일별 Hash와 증거 묶음 Hash를 생성했다.
- 프롬프트·응답·Token·PII를 기본 증거에서 제외했다.
- 가장 좁은 Containment와 Rollback 조건을 기록했다.
- 패치 뒤 차단·인가·Tenant·관측 회귀 테스트를 수행했다.
- 교훈을 Alert, Test, Runbook과 권한 설계에 반영했다.
마무리
LLM 보안 사고 대응의 핵심은 모델이 왜 그런 문장을 만들었는지 추측하는 데 있지 않다. 어떤 주체가 어떤 정책과 배포 버전에서 요청했고, Guardrail과 Application이 무엇을 결정했으며, Tool과 Downstream이 실제로 어떤 Side Effect를 만들었는지를 증거로 연결해야 한다.
Prometheus는 사건의 시점과 규모를, Tempo는 요청의 실행 경로를, Loki와 감사 로그는 결정의 근거와 실제 영향을 보여 준다. 여기에 변경 기록, Hash, 접근통제와 복구 검증을 더하면 관측 데이터는 대응 가능한 증거가 된다. 마지막 단계는 언제나 같은 질문이다. “다음번에는 더 빨리 발견하고, 더 좁게 차단하고, 더 확실하게 복구할 수 있는가?”
공식 자료
- NIST SP 800-61 Rev.3: Incident Response Recommendations and Considerations for Cybersecurity Risk Management
- NIST AI 600-1: Generative Artificial Intelligence Profile
- NIST AI Risk Management Framework
- OpenTelemetry Security
- OpenTelemetry: Handling Sensitive Data
- OpenTelemetry Context Propagation
- Grafana Tempo TraceQL
- Grafana Loki LogQL
- Prometheus Querying Basics
※ 사고 대응·증거 보존·통지 의무는 조직의 산업, 지역, 계약과 법적 요건에 따라 달라진다. 이 글의 명령과 보존 항목은 교육용 예시이며 실제 사건에서는 조직의 승인된 사고 대응 절차와 법무·개인정보 담당자의 지침을 우선한다.
카테고리: 일반IT/IT보안
태그: LLM Security, Incident Response, Digital Forensics, OpenTelemetry, Loki, Tempo, Prometheus, NIST
'일반IT > IT보안' 카테고리의 다른 글
| Presidio와 NeMo Guardrails, AI DLP라고 불러도 될까? (0) | 2026.09.15 |
|---|---|
| 소프트웨어 공급망 보안 입문: 무엇을 공부하고 어떤 도구로 검증할까 (0) | 2026.09.02 |
| Microsoft Presidio 집중 탐구: 개인정보를 LLM에 보내기 전에 왜 필요한가 (0) | 2026.09.02 |
| Application이 지휘할까, NeMo Guardrails가 지휘할까? LLM 보안 오케스트레이션의 책임 분리 (0) | 2026.08.21 |
| LLM으로 KongTuke 난독화 JavaScript 분석하기 — PCAP에서 ClickFix 네트워크 행위까지 (0) | 2026.08.09 |