콘텐츠로 이동

Defense-in-Depth 안전 아키텍처

왜 단일 가드레일로는 부족한가?

섹션 제목: “왜 단일 가드레일로는 부족한가?”

에이전트가 파일 시스템에 접근하고, 코드를 실행하고, 외부 API를 호출하는 시스템에서 단 하나의 안전 장치에 의존하는 것은 매우 위험합니다. 프롬프트 하나로 모든 위험을 막으려 한다면, 그 프롬프트가 우회되거나 잘못 해석되는 순간 시스템 전체가 무방비 상태가 됩니다.

Defense-in-Depth는 군사·보안 분야에서 검증된 원칙입니다. 한 레이어가 뚫려도 다음 레이어가 막습니다. 에이전트 하네스에 이 원칙을 적용하면 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 │ ← 지시 수준 정책
└─────────────────────────────────────────┘

시스템 프롬프트에 보안 정책을 직접 삽입하는 방식입니다. 가장 기본적이지만 단독으로는 가장 취약합니다.

시스템 프롬프트 예시:
- 사용자의 명시적 확인 없이 파일을 삭제하지 마시오
- /etc, /sys 경로에는 절대 접근하지 마시오
- 외부 네트워크 요청 전 반드시 목적을 명시하시오

에이전트가 지시를 “해석”하는 과정에서 의도치 않은 행동이 발생할 수 있으므로, 이 레이어만으로는 충분하지 않습니다.

에이전트에게 도구 목록을 제공할 때 애초에 위험한 도구를 제거하는 방식입니다. Plan 모드에서는 실행 도구를 whitelist에서 제외하고, 서브에이전트별로 필요한 도구만 제공합니다.

에이전트 역할허용 도구제거된 도구
분석 전용 에이전트read_file, searchwrite_file, exec_shell
코드 작성 에이전트write_file, read_fileexec_shell, delete_file
테스트 실행 에이전트exec_shell (제한된 경로)delete_file, network

에이전트가 실제로 도구를 호출하려는 순간, 사람의 승인을 요청하는 관문입니다. Manual, Semi-auto, Auto 세 단계로 구성됩니다 (다음 챕터에서 상세히 다룹니다).

에이전트: "delete_file('/config/database.yml')을 실행하려 합니다"
시스템: [APPROVAL REQUIRED] 승인하시겠습니까? (y/n)
사용자: n
시스템: 실행 취소됨. 에이전트에게 거부 사실 전달

도구 실행 레이어 자체에서 위험 패턴을 감지하고 차단합니다.

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 감지 (파일을 읽은 후 오랜 시간이 지나 편집 시도), 출력 크기 제한 (토큰 폭발 방지) 등도 이 레이어에서 처리합니다.

도구 실행 전후에 사용자 정의 로직을 삽입하는 훅 시스템입니다. 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개의 독립 레이어(프롬프트 → 스키마 → 런타임 승인 → 도구 검증 → 라이프사이클 훅)를 통해 단일 실패점 의존을 제거합니다. 한 레이어가 실패해도 나머지 레이어가 에이전트의 위험한 행동을 차단합니다. 에이전트 자율도가 높아질수록 이 다층 구조의 중요성은 더욱 커집니다.