Harness Engineering Series — Vol.4

닿을 수 없으면,
뚫릴 수도 없다

에이전트가 닿을 수 있는 범위가 곧 공격 표면이다.
5개의 제어축으로 도구의 경계를 설계하면, 실수든 공격이든 침투할 경로 자체가 사라진다.

5개 방향의 나침반과 각각의 경계벽 — 도구 접근 제어 메타포
01 — 지난 이야기

Vol.1-3 리캡:
지금까지 뭘 제어했나

Vol.제어한 것메커니즘핵심 질문
Vol.1행동 차단Hook exit code뭘 할 수 있는지?
Vol.2역할 분리tools 필드누가 하는지?
Vol.3평가 객관화Evaluator + rubric얼마나 잘 했는지?
Vol.4도구 경계 + 보안5개 제어축어디까지 닿을 수 있는지?
Vol.2에서 tools: Read, Write로 내장 도구를 제한했어요. 하지만 에이전트가 Bash를 통해 서버에 SSH하거나, MCP로 데이터베이스에 접근하거나, 다른 에이전트를 스폰해서 배포하는 것까지는 제어하지 못했거든요. Vol.4에서는 에이전트가 외부 세계와 접촉하는 모든 경로의 경계를 설계합니다 — 그리고 이건 자연스럽게 에이전트 보안이 돼요.
Vol.1 Vol.2 Vol.3
02 — 공격 표면

경계 없는 에이전트의
공격 표면

공격 표면(Attack Surface)이란 외부에서 시스템에 침투할 수 있는 모든 경로를 말해요. 에이전트에게 권한이 많을수록 공격 표면이 넓어지고, 실수든 악의적 공격이든 피해 범위가 커져요.

Bash 무제한

editor 에이전트가 문법 수정 중 git push --force를 실행해서 main 브랜치를 덮어씀

보안 위협: 프롬프트 인젝션으로 악성 셸 명령 실행 가능

MCP 무제한

researcher 에이전트가 조사 중 Slack MCP로 "#general 채널"에 미완성 초안을 전송함

보안 위협: 악성 MCP 서버가 민감 데이터를 외부로 유출

Agent 스폰 무제한

creator 에이전트가 자체적으로 deployer 에이전트를 스폰해서 프로덕션에 배포함

보안 위협: 권한 에스컬레이션 — 낮은 권한이 높은 권한을 획득

Permission 없음

fact-checker 에이전트가 검증 중 rm -rf /tmp/*로 임시 파일을 전부 삭제함

보안 위협: 파일시스템 파괴, 서비스 거부(DoS) 공격
이 모든 사고의 공통점이 뭘까요? 에이전트에게 "필요 이상의 접근 권한"이 있었다는 거예요. 보안에서 이걸 최소 권한 원칙(Principle of Least Privilege)이라고 하는데, 쉽게 말하면 "꼭 필요한 것만 허용하고, 나머지는 전부 막는다"는 뜻이에요. 이게 하네스 보안의 본질이에요.
03 — 전체 지도

5개 축으로 보는
도구 제어 전체 지도

Claude Code에서 에이전트의 도구 접근을 제어하는 메커니즘은 정확히 5개예요. 각각 독립적으로 작동하고, 서로 겹치지 않아요. 보안 용어로는 이걸 심층 방어(Defense in Depth)라고 해요 — 여러 겹의 방어선을 쌓아서 하나가 뚫려도 다음 층이 막아주는 구조예요.

축 5 — settings.json
프로젝트/팀 레벨 글로벌 거부 규칙
"이건 누구도 못 한다"
축 4 — permissionMode
도구 사용 승인 프로세스
"쓰려면 허락을 받아라"
축 3 — mcpServers
에이전트별 MCP 서버 스코핑
"이 외부 서비스만 닿을 수 있다"
축 2 — Agent(...) 스코핑
서브에이전트 스폰 권한
"이 동료만 부를 수 있다"
축 1 — tools / disallowedTools
내장 도구 화이트리스트/블랙리스트
"이 도구만 쓸 수 있다"
제어 대상설정 위치막는 보안 위협
1내장 도구에이전트 프론트매터불필요한 파일 수정/삭제
2에이전트 스폰에이전트 프론트매터권한 에스컬레이션
3MCP 서버에이전트 프론트매터데이터 유출, 악성 MCP
4승인 방식에이전트 프론트매터무분별한 자동 실행
5글로벌 정책settings.json조직 전체 위험 명령

+ 보너스: PreToolUse Hook은 런타임 조건부 차단으로, Vol.1에서 이미 다뤘어요. 위 5개 정적 제어 + Hook 동적 제어를 조합하면 완성형 보안 하네스가 돼요.

04 — 축 1

tools / disallowedTools
내장 도구 제어

Vol.2에서 배운 tools 화이트리스트의 반대편 — disallowedTools 블랙리스트도 있어요. 쉽게 말하면 "이것만 쓸 수 있다" vs "이것만 못 쓴다"의 차이예요.

화이트리스트 — tools

"이것만 쓸 수 있다"

명시한 도구만 허용하고, 나머지는 전부 차단해요. Vol.2에서 이미 사용한 방식이에요. 보안 관점에서는 이게 더 안전해요 — 허용한 것만 되니까요.

researcher.md
tools: Read, Grep, Glob, WebSearch, WebFetch
# → Write, Edit, Bash, Agent 전부 사용 불가
# → 파일 수정도, 명령 실행도, 에이전트 스폰도 못 함
블랙리스트 — disallowedTools

"이것만 못 쓴다"

명시한 도구만 차단하고, 나머지는 전부 허용해요. 대부분의 도구를 쓰되 위험한 것만 빼고 싶을 때 편해요. 다만 새로운 도구가 추가되면 자동으로 허용되니까, 화이트리스트보다 보안이 느슨해요.

analyst.md
disallowedTools: Write, Bash
# → Read, Edit, Grep, Glob, WebSearch... 전부 사용 가능
# → 파일 생성과 명령 실행만 차단
보안 팁: 화이트리스트(tools)가 블랙리스트(disallowedTools)보다 안전해요. 새로운 도구가 추가돼도 명시적으로 허용하지 않으면 쓸 수 없으니까요.
05 — 축 2 + 3

에이전트 스폰과
MCP 서버 스코핑

누구를 부를 수 있는지(축 2), 어떤 외부 서비스에 닿을 수 있는지(축 3)를 제어해요. 보안 용어로는 수평 이동(Lateral Movement) 차단이에요 — 한 에이전트가 다른 에이전트나 서비스로 옮겨가면서 권한을 확장하는 걸 막는 거예요.

축 2 — Agent(...) 스코핑

스폰할 수 있는 서브에이전트 제한

coordinator가 worker와 researcher만 부를 수 있고, deployer는 못 부르게 제한하는 거예요. 이러면 coordinator가 아무리 "배포해야 한다"고 판단해도 deployer를 스폰할 수 없어요.

coordinator.md
tools: Agent(worker, researcher), Read, Bash
# → deployer 에이전트는 스폰 불가
# → 프로덕션 배포 사고 원천 차단
보안 위협 차단: 권한 에스컬레이션(Privilege Escalation) — 낮은 권한의 에이전트가 높은 권한의 에이전트를 스폰해서 제한을 우회하는 공격
축 3 — mcpServers 스코핑

에이전트별 MCP 서버 접근 제한

MCP(Model Context Protocol)는 에이전트가 외부 서비스(DB, 브라우저, Slack 등)에 접속하는 통로예요. 이 통로를 에이전트마다 다르게 열어줄 수 있어요.

db-reader.md — 특정 MCP만 허용
mcpServers:
  - supabase

# → 메인 세션의 supabase MCP만 재사용 가능
# → Slack, GitHub, 브라우저 등 다른 MCP는 접근 불가
# → DB는 읽되, 다른 곳에 데이터를 보낼 수 없음
browser-tester.md — 전용 MCP 인라인 정의
mcpServers:
  - playwright:
      type: stdio
      command: npx
      args: ["-y", "@playwright/mcp@latest"]

# → 이 에이전트에서만 Playwright MCP 연결
# → 에이전트 종료 시 자동 해제 (context 절약)
보안 위협 차단: 데이터 유출(Data Exfiltration) — DB에서 읽은 민감 정보를 Slack이나 외부 API로 전송하는 공격. MCP를 분리하면 "읽는 통로"와 "보내는 통로"를 다른 에이전트에 배치할 수 있어요.
06 — 축 4 + 5

승인 프로세스와
글로벌 정책

도구를 허용하되 "어떻게 사용하는지"를 제어하는 축 4와, 프로젝트 전체에 거부 규칙을 강제하는 축 5예요.

축 4 — permissionMode

도구 사용 승인 방식

같은 Bash 도구라도, 어떤 에이전트는 자동으로 실행하고, 어떤 에이전트는 매번 사용자에게 "이거 실행해도 돼요?" 하고 물어보게 만들 수 있어요.

모드동작보안 수준
plan읽기만 가능 (탐색 모드)가장 엄격
dontAsk미승인 도구 자동 거부엄격
acceptEdits파일 편집만 자동 수락중간
bypassPermissions모든 권한 프롬프트 생략가장 느슨
보안 팁: 신뢰할 수 없는 작업에는 plan이나 dontAsk를, 자동화 파이프라인에서만 bypassPermissions를 사용하세요.
축 5 — settings.json permissions

프로젝트 전체 거부/허용 규칙

에이전트 개별 설정이 아니라, 프로젝트 레벨에서 "이건 누구도 못 한다"를 강제하는 거예요. 보안 용어로는 조직 정책(Organization Policy)이에요 — 개인이 아무리 바꾸려 해도 조직 규칙이 우선해요.

.claude/settings.json
{
  "permissions": {
    "deny": [
      "Bash(git push *)",       // 원격 푸시 차단
      "Bash(rm -rf *)",         // 재귀 삭제 차단
      "Bash(docker rm *)",      // 컨테이너 삭제 차단
      "Agent(deployer)"         // deployer 스폰 차단
    ],
    "allow": [
      "Bash(git status)",
      "Bash(git diff *)",
      "Bash(git log *)",
      "Bash(git add *)",
      "Bash(git commit *)"
    ]
  }
}

평가 순서: denyaskallow. deny가 최우선이라, allow에 있어도 deny에 있으면 무조건 차단돼요.

07 — 실습

직접 해보기:
settings.json으로 git push 차단

축 5(settings.json)를 직접 설정하고, 에이전트가 git push를 시도할 때 차단되는 걸 확인해봐요.

Step 1. 프로젝트 구조 만들기

my-project/
  ├── .claude/
  │   ├── settings.json  ← 거부 규칙 설정
  │   └── agents/
  │       └── writer.md  ← git push 시도하는 에이전트
  └── (git init 필요)

Step 2. settings.json 작성

.claude/settings.json
{
  "permissions": {
    "deny": ["Bash(git push *)", "Bash(rm -rf *)"],
    "allow": [
      "Bash(git status)", "Bash(git add *)",
      "Bash(git commit *)", "Bash(echo *)"
    ]
  }
}

Step 3. writer 에이전트 작성

.claude/agents/writer.md
---
name: writer
description: 파일을 작성하고 git에 커밋하는 에이전트
tools: Read, Write, Bash
---

요청받은 내용을 파일로 작성하고 git에 커밋한다.
커밋 후 git push origin master로 원격에 푸시한다.

Step 4. 실행 결과 (실제 테스트)

CLAUDE CODE — 실제 실행 결과
U
"writer 에이전트에게 위임해서 hello.md를 작성하고, git 커밋하고, git push origin master로 푸시해줘"
── writer 에이전트 동작 ──
W
hello.md 작성 완료
git add hello.md
git commit -m "Add hello.md"
── git push 시도 ──
!
git push권한 설정에 의해 차단되었습니다.
로컬 작업(파일 생성 + 커밋)은 모두 완료된 상태이니, 터미널에서 직접 실행해주세요:
git push origin master
이게 하네스의 힘이에요. writer 에이전트의 프롬프트에는 "git push로 푸시하라"고 분명히 적혀 있어요. 하지만 settings.jsondeny: ["Bash(git push *)"] 규칙이 인프라 레벨에서 차단했어요. 프롬프트로 "푸시하지 마"라고 부탁한 게 아니라, 물리적으로 못 하게 막은 거예요.
보안 관점에서 보면: 만약 누군가가 프롬프트 인젝션으로 에이전트에게 git push --force를 실행시키려 해도, settings.json의 deny 규칙이 먼저 평가되기 때문에 실행 자체가 불가능해요. 에이전트의 "의지"와 무관하게 인프라가 차단하는 거예요.
08 — 마무리

하네스 아키텍처
전체 지도

Vol.질문메커니즘보안 원칙
1뭘 할 수 있는지?Hook exit code런타임 검증
2누가 하는지?tools 필드역할 기반 접근 제어
3얼마나 잘 했는지?Evaluator + rubric독립 감사
4어디까지 닿는지?5개 제어축최소 권한 + 심층 방어

최소 권한이 최강 보안이다

Vol.1~4를 관통하는 하나의 원칙이 있어요 — 에이전트에게 "필요한 만큼만" 허용하고, 나머지는 전부 차단하는 것.

이건 단순한 실수 방지가 아니라, 에이전트 시대의 보안 아키텍처예요. 프롬프트 인젝션이든, 악성 MCP 서버든, 권한 에스컬레이션이든 — 애초에 닿을 수 없으면 뚫릴 수도 없거든요.

프롬프트로 "조심해줘"라고 부탁하는 건 방화벽 없이 직원에게 "해커 조심해"라고 말하는 거랑 같아요. 인프라로 강제해야 보안이에요.

행동 차단(Hook)
+ 역할 분리(tools)
+ 평가 객관화(Evaluator)
+ 도구 경계(5개 제어축)
= 에이전트 보안 하네스
Dante Labs 자료실에서 보기

마크다운 전문 + 시리즈 전체 링크가 포함된 블로그 버전