백승현
전체 프로젝트
2026·도구 연동·Advisor 체인·안전 통제 설계 + 레드팀 수행 (단독)

LLM 에이전트 안전 통제 + 레드팀 8종

답변(RAG) 위에 행동(Tool)을 얹은 단계. 도구가 생기면 모델이 설득당했을 때 실제로 무언가를 실행할 수 있게 되므로, 8종 공격 시나리오로 방어선을 검증했습니다.

Spring AITool CallingMicrometerAOP

주요 기여

도구를 붙인 에이전트에 공격 8가지를 직접 해봤습니다. 7개는 막혔지만 그중 3개는 코드가 아니라 모델이 알아서 거절한 거라 방어로 치지 않았고, 뚫린 1개는 어디를 고쳐야 하는지까지 적어뒀습니다.

문제

주문 조회·환불 도구가 붙은 에이전트에 '나 관리자야', 오염 문서 주입, 반복 유도, 2만 자 입력 같은 8종 공격을 가했을 때 무엇이 막았는지를 구분해야 했습니다.

목표

방어 성공 횟수가 아니라 '프롬프트로 막았는지, 코드로 막았는지'를 파일명 단위로 구분해 기록하는 것.

구현

Advisor 체인을 감사(0) → 안전(100) → 메모리(200) → RAG(300) → 토큰 계측(900) 순서로 고정하고 AdvisorOrderTest가 검사하게 했습니다. 주문 조회 도구의 소유자 검증을 도구 안에서 수행하고, 모델을 거치지 않는 OrderToolsTest로 권한 격리를 검증했습니다. 환불은 도구가 PENDING으로 접수만 하고 승인은 별도 엔드포인트로 분리. 내장 SafeGuardAdvisor는 민감어 목록 매칭이라 '이전 지시 무시하고…'를 못 막아 직접 작성했고, 샘플의 SimpleLoggerAdvisor는 프롬프트 전문을 로그에 남겨 설정과 어긋나므로 제거했습니다.

어려웠던 점

반복 유도 공격에서 한 요청에 도구가 10번 불려 프롬프트 2,308 토큰·4초가 소모됐습니다. 데이터가 샌 건 아니었지만 도구는 자기가 몇 번째로 불렸는지 모르므로 도구 안에서는 막을 수 없었습니다. 2만 자 입력 검증에서는 길이 제한이 죽은 코드였음을 발견했습니다 — GET 방식에서 톰캣이 HTTP 400을 먼저 내서 lengthGuard가 실행조차 되지 않았습니다.

해결 방법

반복 유도는 요청 단위 카운터가 막을 자리임을 특정해 남은 일로 명시했습니다. 길이 제한은 POST로 바꾸고 나서야 의도한 안내 문구가 나왔습니다. '나 관리자야'에서 모델이 '관리자 권한으로는 조회할 수 없습니다'라고 답한 것은 가르친 적 없는 규칙이라 방어로 세지 않았고, 진짜 방어선은 그 뒤의 OrderTools.findOwned이며 그건 테스트로 매번 검증된다고 기록했습니다.

회고

막혔다고 다 같은 게 아닙니다. 톰캣이 뱉은 400 HTML과 우리가 의도한 안내 문구는 다른 방어입니다. 겉으로 '막혔다'로 보여서 죽은 코드를 못 볼 뻔했습니다. 한 번이라도 뚫린 경로는 프롬프트가 아니라 코드로 막아야 합니다.