Defense-in-Depth 안전 아키텍처
왜 단일 가드레일로는 부족한가?
섹션 제목: “왜 단일 가드레일로는 부족한가?”에이전트가 파일 시스템에 접근하고, 코드를 실행하고, 외부 API를 호출하는 시스템에서 단 하나의 안전 장치에 의존하는 것은 매우 위험합니다. 프롬프트 하나로 모든 위험을 막으려 한다면, 그 프롬프트가 우회되거나 잘못 해석되는 순간 시스템 전체가 무방비 상태가 됩니다.
Defense-in-Depth는 군사·보안 분야에서 검증된 원칙입니다. 한 레이어가 뚫려도 다음 레이어가 막습니다. 에이전트 하네스에 이 원칙을 적용하면 5개의 독립적인 안전 레이어를 구성할 수 있습니다.
5계층 방어 모델
섹션 제목: “5계층 방어 모델”┌─────────────────────────────────────────┐│ Layer 5: Lifecycle Hooks │ ← 실행 전/후 검사├─────────────────────────────────────────┤│ Layer 4: Tool-level Validation │ ← 도구 호출 시 검증├─────────────────────────────────────────┤│ Layer 3: Runtime Approval System │ ← 사람의 승인 관문├─────────────────────────────────────────┤│ Layer 2: Schema-level Restrictions │ ← 구조적 도구 제한├─────────────────────────────────────────┤│ Layer 1: Prompt-level Guardrails │ ← 지시 수준 정책└─────────────────────────────────────────┘Layer 1 — Prompt-level Guardrails
섹션 제목: “Layer 1 — Prompt-level Guardrails”시스템 프롬프트에 보안 정책을 직접 삽입하는 방식입니다. 가장 기본적이지만 단독으로는 가장 취약합니다.
시스템 프롬프트 예시:- 사용자의 명시적 확인 없이 파일을 삭제하지 마시오- /etc, /sys 경로에는 절대 접근하지 마시오- 외부 네트워크 요청 전 반드시 목적을 명시하시오에이전트가 지시를 “해석”하는 과정에서 의도치 않은 행동이 발생할 수 있으므로, 이 레이어만으로는 충분하지 않습니다.
Layer 2 — Schema-level Restrictions
섹션 제목: “Layer 2 — Schema-level Restrictions”에이전트에게 도구 목록을 제공할 때 애초에 위험한 도구를 제거하는 방식입니다. Plan 모드에서는 실행 도구를 whitelist에서 제외하고, 서브에이전트별로 필요한 도구만 제공합니다.
| 에이전트 역할 | 허용 도구 | 제거된 도구 |
|---|---|---|
| 분석 전용 에이전트 | read_file, search | write_file, exec_shell |
| 코드 작성 에이전트 | write_file, read_file | exec_shell, delete_file |
| 테스트 실행 에이전트 | exec_shell (제한된 경로) | delete_file, network |
Layer 3 — Runtime Approval System
섹션 제목: “Layer 3 — Runtime Approval System”에이전트가 실제로 도구를 호출하려는 순간, 사람의 승인을 요청하는 관문입니다. Manual, Semi-auto, Auto 세 단계로 구성됩니다 (다음 챕터에서 상세히 다룹니다).
에이전트: "delete_file('/config/database.yml')을 실행하려 합니다"시스템: [APPROVAL REQUIRED] 승인하시겠습니까? (y/n)사용자: n시스템: 실행 취소됨. 에이전트에게 거부 사실 전달Layer 4 — Tool-level Validation
섹션 제목: “Layer 4 — Tool-level Validation”도구 실행 레이어 자체에서 위험 패턴을 감지하고 차단합니다.
DANGEROUS_PATTERNS = [ r'rm\s+-rf\s+/', # 루트 삭제 r'chmod\s+777', # 전체 권한 부여 r'curl.*\|\s*sh', # 원격 스크립트 실행 r'eval\s*\(', # 동적 코드 실행]
def validate_shell_command(cmd: str) -> bool: for pattern in DANGEROUS_PATTERNS: if re.search(pattern, cmd): raise SecurityError(f"위험 패턴 감지: {pattern}") return True또한 stale-read 감지 (파일을 읽은 후 오랜 시간이 지나 편집 시도), 출력 크기 제한 (토큰 폭발 방지) 등도 이 레이어에서 처리합니다.
Layer 5 — Lifecycle Hooks
섹션 제목: “Layer 5 — Lifecycle Hooks”도구 실행 전후에 사용자 정의 로직을 삽입하는 훅 시스템입니다. Claude Code의 PreToolUse / PostToolUse 훅이 대표적입니다.
{ "hooks": { "PreToolUse": [ { "matcher": "Bash", "hooks": [{ "type": "command", "command": "validate-bash-command.sh" }] } ], "PostToolUse": [ { "matcher": "Write", "hooks": [{ "type": "command", "command": "prettier --write $FILE" }] } ] }}훅이 0이 아닌 exit code를 반환하면 해당 도구 실행이 차단됩니다.
각 레이어의 독립성이 중요한 이유
섹션 제목: “각 레이어의 독립성이 중요한 이유”| 레이어 | 우회 시나리오 | 다음 방어선 |
|---|---|---|
| 프롬프트 가드레일 | 프롬프트 인젝션 공격 | 스키마 제한 |
| 스키마 제한 | 허용 도구의 악용 | 런타임 승인 |
| 런타임 승인 | Auto 모드 오설정 | 도구 레벨 검증 |
| 도구 레벨 검증 | 알려지지 않은 패턴 | 라이프사이클 훅 |
| 라이프사이클 훅 | 훅 스크립트 버그 | 외부 모니터링 |
Defense-in-Depth는 5개의 독립 레이어(프롬프트 → 스키마 → 런타임 승인 → 도구 검증 → 라이프사이클 훅)를 통해 단일 실패점 의존을 제거합니다. 한 레이어가 실패해도 나머지 레이어가 에이전트의 위험한 행동을 차단합니다. 에이전트 자율도가 높아질수록 이 다층 구조의 중요성은 더욱 커집니다.