보안 AI 에이전트 설계: Indirect Prompt Injection 공격에 대한 System-Level 방어 전략
Architecting Secure AI Agents: Perspectives on System-Level Defenses Against Indirect Prompt Injection Attacks
TL;DR Highlight
AI 에이전트가 외부 데이터에 숨겨진 악성 명령에 당하지 않으려면 동적 플래닝, LLM 입력 제한, 인간 개입을 함께 설계해야 한다.
Who Should Read
LLM 기반 에이전트(코딩 어시스턴트, 이메일 처리 봇 등)를 프로덕션에 배포하려는 백엔드/ML 엔지니어. 외부 웹페이지, 이메일, 툴 출력 등 신뢰할 수 없는 데이터를 다루는 에이전트 시스템의 보안을 고민하는 개발자.
Core Mechanics
- Indirect Prompt Injection(에이전트가 읽는 외부 데이터에 악성 명령을 숨기는 공격)은 현재 가장 큰 에이전트 보안 위협이고, 기존 방어법들은 각각 한계가 있음
- Plan-execution isolation(계획 고정 실행)은 API 버전 변경이나 런타임 오류 같은 현실적 상황에서 에이전트가 작동을 멈추게 만들어서 실용성이 없음
- LLM을 보안 판단에 써야 할 때는 raw 환경 텍스트 전체를 주지 말고, 구조화된 diff/trace만 넘겨서 판단 범위를 좁혀야 공격 표면이 줄어듦
- LLM이 따르려는 instruction을 먼저 말로 표현하게 하고(verbalize), 그 출처(신뢰 vs 비신뢰)를 추적해서 시스템 레벨에서 실행 여부를 결정하는 2단계 분리 방식 제안
- LLM을 시키는 것: 각 실행 단계별 프로그래밍 검증기(validator)를 자동 생성해서 환경 응답을 확인하는 방식도 유효(예: 특정 DOM 경로의 숫자만 추출하는 regex 생성)
- 기존 AgentDojo 같은 벤치마크는 static 공격 페이로드만 쓰고 replanning이 필요한 복잡한 태스크가 거의 없어서, 방어 성능을 실제보다 훨씬 낙관적으로 평가하게 만듦
Evidence
- AgentDojo 벤치마크 97개 태스크 중 replanning/policy update가 필요한 태스크는 단 6개(6%)뿐 — 현실 시나리오를 반영 못 함
- 기존 벤치마크(AgentDojo, InjecAgent, ASB 등) 전부 static 공격 페이로드만 사용, RL 기반 adaptive attacker나 genetic algorithm 최적화 공격은 미포함
- CaMeL의 send_money 정책처럼 task-agnostic 전역 정책은 이메일 첨부 청구서에서 수신자 정보를 읽는 정상 케이스도 차단하는 false positive 발생
- Progent는 환경 피드백을 무제한으로 LLM에 넘겨 policy를 업데이트하는 구조라, policy adjuster LLM을 직접 공략하는 adaptive attack에 취약
How to Apply
- 에이전트가 외부 소스(웹페이지, 이메일 등)에서 읽은 instruction을 실행하기 전에, LLM이 '따르려는 명령을 명시적으로 반복하게' 만들고, 그 출처가 신뢰 소스인지 확인 후 시스템 코드로 차단/허용을 결정하는 2단계 파이프라인을 구현한다
- 환경 응답을 LLM executor에 raw text로 넘기지 말고, 각 단계 시작 전에 LLM에게 '이 단계에서 유효한 응답 형식의 validator 코드'를 생성하게 한 뒤, 응답을 그 validator로 먼저 필터링한 구조화된 결과만 넘긴다 (예: Q4 매출 숫자만 추출하는 regex + DOM path 명시)
- 에이전트 계획(plan) 변경이 필요할 때, 변경 전후의 diff와 실행 이력 요약만 포함한 구조화된 JSON을 LLM 보안 판단 모델에 넘겨 '이 변경이 원래 태스크와 맥락상 합리적인가'를 판단하게 하되, 원본 환경 텍스트는 절대 포함하지 않는다
Code Example
# Proposal 1: Instruction verbalization + provenance check 패턴
def process_external_content(content: str, trusted_sources: list[str]) -> dict:
"""
외부 컨텐츠에서 LLM이 따르려는 instruction을 표면화하고
출처 기반으로 실행 허용 여부를 결정
"""
# Step 1: LLM에게 '따르려는 명령'을 명시적으로 verbalize하게 요청
verbalize_prompt = f"""
다음 텍스트를 읽고, 당신이 실행해야 한다고 판단되는 명령(instruction)이 있다면
그것만 정확히 리스트로 반복해서 출력하세요. 없으면 빈 리스트를 반환하세요.
텍스트: {content}
출력 형식: {{"instructions": ["명령1", "명령2"]}}
"""
verbalized = llm_call(verbalize_prompt) # {"instructions": ["install package X", ...]}
# Step 2: 각 instruction의 출처 추적 (substring matching)
result = []
for instruction in verbalized["instructions"]:
# 해당 instruction이 신뢰 소스에서 왔는지 확인
source = "trusted" if any(instruction in src for src in trusted_sources) else "untrusted"
result.append({"instruction": instruction, "source": source})
return result
def policy_enforcer(instructions: list[dict], mode: str = "block_external") -> list[dict]:
"""
Mode 1: 외부 instruction 전부 차단
Mode 2: 외부 instruction은 사용자 승인 필요
"""
allowed = []
for item in instructions:
if item["source"] == "trusted":
allowed.append(item)
elif mode == "block_external":
print(f"[BLOCKED] 외부 출처 instruction 차단: {item['instruction']}")
elif mode == "require_approval":
user_ok = input(f"외부 instruction 실행 허용? '{item['instruction']}' (y/n): ")
if user_ok.lower() == 'y':
allowed.append(item)
return allowed
# Proposal 2: LLM으로 step-specific validator 자동 생성
def generate_step_validator(task_step: str, expected_output_desc: str) -> str:
"""
각 실행 단계에 맞는 deterministic 검증 코드를 LLM이 생성
"""
prompt = f"""
다음 에이전트 태스크 단계에서 환경 응답을 검증하는 Python 함수를 작성하세요.
악성 텍스트를 무시하고 필요한 데이터만 추출해야 합니다.
태스크 단계: {task_step}
기대 출력: {expected_output_desc}
함수 시그니처: def validate_response(response: str) -> dict | None
- 유효하면 추출된 데이터 dict 반환
- 유효하지 않으면 None 반환
"""
validator_code = llm_call(prompt)
return validator_code
# 사용 예시
step = "Q4 revenue를 웹페이지에서 추출"
expected = "숫자 형식의 매출액 (예: $1,234,567)"
validator = generate_step_validator(step, expected)
# 생성된 validator를 exec()으로 실행해 환경 응답 검증
# import re
# def validate_response(response):
# table_match = re.search(r'Q4.*?\$(\d[\d,.]+)', response, re.DOTALL)
# if table_match: return {"revenue": table_match.group(1)}
# return NoneTerminology
Related Resources
- CaMeL: Defeating prompt injections by design
- AgentDojo: Dynamic environment to evaluate prompt injection
- Progent: Programmable privilege control for LLM agents
- AgentDyn: Dynamic open-ended benchmark for agent security
- Instruction-following intent analysis (저자 prior work)
- DRIFT: Dynamic rule-based defense with injection isolation
Original Abstract (Expand)
AI agents, predominantly powered by large language models (LLMs), are vulnerable to indirect prompt injection, in which malicious instructions embedded in untrusted data can trigger dangerous agent actions. This position paper discusses our vision for system-level defenses against indirect prompt injection attacks. We articulate three positions: (1) dynamic replanning and security policy updates are often necessary for dynamic tasks and realistic environments; (2) certain context-dependent security decisions would still require LLMs (or other learned models), but should only be made within system designs that strictly constrain what the model can observe and decide; (3) in inherently ambiguous cases, personalization and human interaction should be treated as core design considerations. In addition to our main positions, we discuss limitations of existing benchmarks that can create a false sense of utility and security. We also highlight the value of system-level defenses, which serve as the skeleton of agentic systems by structuring and controlling agent behaviors, integrating rule-based and model-based security checks, and enabling more targeted research on model robustness and human interaction.