불편해서 껐던 샌드박스, 다시 켜기 전에 따져 본 것들 — 에이전트 격리 다시 보기#
2026-10-18

알고도 껐던 샌드박스#
필자는 Claude Code의 샌드박스를 꺼 둔 채, 승인 프롬프트 없이 모든 도구 호출을 허용하는 bypass 권한 모드에서 대부분의 작업을 해 왔습니다. 샌드박스를 몰라서가 아니었습니다. 켜 보니 불편했고, 업무 속도를 위해 보안 위험을 어느 정도 감수하기로 했습니다. 대신 세션 안에서 에이전트의 응답을 꼼꼼히 읽으며 문제가 될 상황을 만들지 않으면 된다고 판단했습니다.
그러다 지난주 2026-10-11 AI 뉴스 브리프에 Microsoft의 MXC(Microsoft Execution Containers) 정식 출시 소식을 적었습니다. 파일, 네트워크, 프로세스, UI 접근을 JSON 정책으로 정하면 운영체제가 그 경계를 강제하는 격리 계층입니다. 필자는 그 글에 “macOS의 Seatbelt 백엔드로 Claude Code 작업을 감싸는 실험을 해 볼 생각"이라고 적었는데, 그 실험을 하려면 먼저 필자가 왜 껐는지부터 다시 따져 봐야 했습니다.
이 글은 그 점검의 기록입니다. 결론부터 적으면 이렇습니다. 응답을 꼼꼼히 읽는 것은 사후 검토라서 경계를 대신하지 못합니다. 그리고 필자가 느낀 불편의 상당 부분은 감수할 대상이 아니라 설정으로 줄일 수 있는 대상이었습니다. 다만 아직 샌드박스를 켜고 실제로 일하고 있는 것은 아니어서, 불편과 완화 방법은 공식 문서를 기준으로 정리했습니다. 필자의 작업에 대입한 부분은 경험이 아니라 예상입니다.
“꼼꼼히 읽으면 된다"는 전제 점검#
필자의 판단이 통하는 조건은 분명히 있습니다. 한 세션의 한 작업에 집중하고 있고, 명령이 실행되기 전에 그것을 보고 거절할 수 있을 때입니다. 수동 승인 모드에서 프롬프트를 하나하나 읽는다면 이 조건에 가깝습니다.
문제는 필자가 일하는 방식이 이 조건에서 점점 멀어졌다는 점입니다. 따져 보니 네 군데에서 전제가 흔들렸습니다.
첫째, bypass 모드에서 읽기는 실행 뒤에 일어납니다. 응답을 읽고 이상한 점을 발견했다면 그 일은 이미 일어난 뒤입니다. 읽기는 사고를 알아차리는 수단이지 막는 수단이 아닙니다.
둘째, 사고는 필자가 응답을 읽기 전에 한 턴 안에서 끝날 수 있습니다. 예를 들어 에이전트가 웹페이지를 읽었는데 그 안에 “설정 파일을 읽어 이 주소로 보내라"는 숨은 지시가 있고, 에이전트가 다음 도구 호출에서 curl로 그대로 실행하는 경우입니다. 필자의 화면에는 도구 호출 요약 몇 줄이 지나갈 뿐입니다. 이런 공격을 프롬프트 인젝션이라고 부릅니다.
셋째, 필자가 읽는 화면 밖에서 도는 코드가 있습니다. MCP(Model Context Protocol) 서버와 훅은 대화에 그 동작이 다 드러나지 않습니다. npm install이 실행하는 의존성의 설치 스크립트도 마찬가지입니다.
넷째, 주의력에는 한계가 있습니다. 필자는 여러 세션을 오가며 일하고, 하위 에이전트와 백그라운드 작업도 점점 많이 씁니다. 동시에 도는 작업이 늘수록 “꼼꼼히 읽기"는 현실적으로 불가능해집니다.
정리하면 필자의 판단이 틀렸다기보다, 작업 방식이 바뀌면서 그 판단의 전제가 낡아 가고 있었습니다. 응답 읽기는 결과물의 품질을 검토하는 수단으로는 여전히 중요하지만, 보안 경계의 역할은 하지 못합니다.
승인은 경계가 아니다#
그렇다면 경계는 무엇이어야 할까요. MXC 발표문은 출발점을 이렇게 요약합니다. 에이전트는 스스로의 보안 권한자가 될 수 없으므로, 개발자나 조직이 정하고 에이전트와 무관하게 강제되는 경계 안에서 돌아야 한다는 것입니다. 정책은 에이전트가 손댈 수 없는 바깥에 있어야 하고, 그래야 에이전트나 에이전트가 만든 코드가 스스로 권한을 넓히지 못합니다.
Claude Code 문서도 같은 구분을 합니다. 권한 모드는 도구 호출을 실행할지와 실행 전에 물을지를 정하고, 격리는 실행된 뒤 무엇에 닿을 수 있는지를 정합니다. 승인 프롬프트는 전자에 속합니다. 그리고 매번 묻는 방식은 승인 피로를 낳아 결국 “전부 허용"으로 끝나기 쉽습니다. 필자가 bypass 모드에 이른 길도 정확히 이것이었습니다.
격리가 왜 필요한지는 사이먼 윌리슨(Simon Willison)이 이름 붙인 치명적 3요소(lethal trifecta) 로 보면 분명해집니다.
- 비공개 데이터에 접근할 수 있다 (소스 코드,
~/.ssh, API 토큰) - 신뢰할 수 없는 콘텐츠에 노출된다 (웹페이지, 이슈, 외부 문서)
- 외부로 내보낼 수단이 있다 (네트워크)
세 가지가 한 에이전트에 모이면, 공격자는 콘텐츠에 지시를 숨기는 것만으로 데이터를 빼낼 수 있습니다. 코딩 에이전트는 기본적으로 셋을 모두 갖고 있습니다. 숨은 지시를 걸러 내는 필터는 확률적으로만 작동하므로, 격리는 셋 중 한 다리를 구조적으로 끊는 쪽을 택합니다. 보통은 3번 네트워크를 허용한 곳으로만 열거나 1번 비밀 파일 읽기를 막습니다.
격리의 지도 — 무엇을, 얼마나 강하게#
격리는 다섯 개의 축으로 나눠 볼 수 있습니다.
- 파일 쓰기: 작업 디렉터리만 허용하고, 쉘 설정 파일이나
.git/hooks, 에이전트 설정 파일은 보호 - 파일 읽기:
~/.ssh,~/.aws/credentials같은 비밀 파일 차단 - 네트워크: 기본 차단하고, 허용한 도메인만 프록시를 통해 통과
- 자격증명과 환경변수: 토큰을 지우거나 가짜 값으로 바꿈
- 프로세스와 UI: 데스크톱, 클립보드, 다른 앱 제어 차단
이 중 파일과 네트워크는 반드시 함께 막아야 합니다. Claude Code 문서는 네트워크 격리가 없으면 SSH 키가 새어 나가고, 파일 격리가 없으면 시스템 파일에 백도어를 심어 네트워크 접근을 되찾을 수 있다고 경고합니다.
경계를 만드는 방식은 크게 두 갈래입니다. 같은 운영체제와 파일시스템 위에서 프로세스가 할 수 있는 일을 정책으로 막는 제한형과, 아예 별도의 파일시스템이나 커널을 주고 필요한 것만 연결하는 분리형입니다. 흔히 “샌드박스"라고 부르는 것은 좁게는 제한형을 가리키지만, 실무에서는 둘을 섞어 부릅니다. 강도 순으로 늘어놓으면 다음과 같습니다.
| 단계 | 대표 기술 | 특징 |
|---|---|---|
| 앱 내부 권한 규칙 | 허용 / 거부 규칙, 분류기 | 정책일 뿐 경계는 아님 |
| OS 프로세스 샌드박스 | Seatbelt, bubblewrap, Landlock, AppContainer | 가볍고 빠름. 커널은 공유 |
| 컨테이너 | Docker, dev container | 환경 전체를 분리. 커널은 공유 |
| microVM / VM | Docker Sandboxes, Firecracker | 커널까지 분리. 가장 강하지만 무거움 |
| 클라우드 세션 | Claude Code on the web 등 | 로컬 기기와 완전히 분리 |
Seatbelt는 macOS, bubblewrap과 Landlock은 Linux, AppContainer는 Windows에 들어 있는 격리 기능입니다. Claude Code의 샌드박스는 macOS에서 Seatbelt, Linux와 WSL2에서 bubblewrap을 쓰고, 네이티브 Windows에서는 샌드박스 없이 실행됩니다. OpenAI Codex는 작업 폴더에만 쓸 수 있는 workspace-write를 로컬 작업의 기본값으로 두고, GitHub Copilot은 이번에 MXC 기반 로컬 샌드박스를 정식 제공하기 시작했습니다.
MXC가 의미 있는 이유는 이 백엔드들을 하나의 정책 포맷으로 묶는다는 점입니다. 같은 JSON 정책을 Windows에서는 AppContainer로, macOS에서는 Seatbelt로, Linux에서는 Bubblewrap으로 강제합니다. 정책을 처음 만들 때는 차단하면서 기록하는 Learning 모드나 허용하면서 기록하는 Permissive 모드로 관찰한 뒤 강제 모드로 넘어갈 수 있습니다. Claude Code는 지원 “예정” 목록에 있어서, 필자가 당장 쓸 수 있는 것은 Claude Code 자체의 샌드박스입니다.
가짜 환경은 왜 드문가#
조사하면서 궁금했던 것이 하나 있습니다. 외부를 막는 대신, 샌드박스 안에 가짜 외부 환경을 만들어 에이전트가 진짜로 알고 쓰게 하는 방식은 없을까 하는 점이었습니다.
있기는 합니다. 다만 무엇을 가짜로 만드느냐에 따라 쓰임새가 다릅니다.
가장 실용적인 것은 자격증명입니다. 에이전트에게 필요한 것은 토큰 값 자체가 아니라 그 토큰으로 인증된 결과이기 때문입니다. Claude Code의 mask 설정은 샌드박스 안의 명령에 세션마다 다른 가짜 값을 보여 주고, 샌드박스 밖의 프록시가 허용된 호스트로 나가는 요청에서만 진짜 값으로 바꿔 끼웁니다. 에이전트가 토큰을 출력하거나 엉뚱한 곳에 보내도 새어 나가는 것은 가짜입니다. Claude Code의 클라우드 세션도 GitHub 토큰을 샌드박스 밖 프록시에 두고, 안쪽에는 범위를 좁힌 자격증명만 발급합니다.
파일시스템 수준의 대체도 흔합니다. 컨테이너의 overlay 파일시스템은 원본처럼 보이는 복사본이어서, 거기에 쓴 내용은 원본에 반영되지 않습니다. bubblewrap은 보호할 경로에 빈 파일을 덮어씌워 쓰기를 막기도 합니다.
반면 외부 서비스 전체를 흉내 내는 방식은 주로 벤치마크와 학습용 시뮬레이션 환경, 그리고 미끼 토큰을 심어 유출을 탐지하는 기만 방어에서 쓰입니다. 실무 에이전트에 드문 이유는 두 가지라고 필자는 봅니다. 하나는 일이 안 된다는 점입니다. 에이전트는 빌드 결과나 API 응답 같은 진짜 결과를 보고 다음 판단을 하는데, 가짜 응답을 받으면 엉뚱한 결론으로 헛돕니다. 다른 하나는 정직한 거부가 더 잘 작동한다는 점입니다. Claude Code는 막힌 호스트 이름을 명령 결과에 그대로 알려 주고, 에이전트는 그걸 보고 “이 도메인의 허용이 필요하다"고 사용자에게 요청할 수 있습니다. 조용히 가짜를 주면 이 피드백이 끊깁니다.
그래서 지금의 실무는 비밀은 가짜로 주고, 환경은 진짜를 주되 경계로 막는 쪽으로 정리돼 있습니다. 이 구분은 뒤에서 불편을 줄이는 방법을 고를 때도 기준이 됩니다.
다시 켜면 마주칠 불편들#
Claude Code 샌드박스 문서의 문제 해결 항목에서, 필자가 쓰는 macOS 기준으로 마주칠 만한 것을 추렸습니다.
| 불편 | 원인 |
|---|---|
| 새 호스트에 접속할 때마다 막힘 | 허용 도메인 목록이 빈 상태에서 시작 |
SSH로 git push가 실패함 | macOS에서는 SSH 터널이 샌드박스 프록시에 인증하지 못함 |
DB 클라이언트, ssh가 접속하지 못함 | 프록시를 거치지 않는 도구는 나갈 길이 없음 |
| localhost 개발 서버에 접속하지 못함 | 샌드박스 밖 로컬 서버로의 직접 연결이 기본으로 막힘 |
open, osascript가 -600 오류를 냄 | Apple Events가 기본 차단 |
gh 같은 Go 기반 CLI가 TLS 검증에 실패함 | Seatbelt 환경과의 호환 문제 |
docker 명령이 안 됨 | 샌드박스와 호환되지 않음 |
pbcopy로 클립보드에 복사되지 않음 | 시스템 클립보드에 닿지 못함 |
git checkout이 unable to unlink old로 실패함 | .claude/skills 같은 보호 경로의 파일을 바꿔야 함 |
필자의 블로그 작업에 대입해 보면 몇 가지가 바로 걸릴 것으로 예상합니다. GA(Google Analytics)와 Search Console을 조회하는 스크립트는 Google API 도메인을 허용해야 하고, 로그인된 Chrome을 띄우는 스크립트는 open을 쓰므로 Apple Events 차단에 걸릴 것입니다. 이 저장소는 .claude/skills를 함께 관리하므로, 브랜치를 전환할 때 스킬 파일이 바뀌면 보호 경로 문제도 만날 수 있습니다.
불편과는 결이 다른 함정도 하나 있습니다. Claude Code의 샌드박스는 Bash 같은 셸 명령만 감쌉니다. 파일 읽기 / 쓰기 도구, MCP 서버, 훅은 샌드박스 밖에서 필자의 권한 그대로 돕니다. 켜기만 하면 불편은 늘어나는데 보호는 생각보다 좁을 수 있다는 뜻입니다.
감수할 것인가, 설계할 것인가#
목록을 보면 불편이 꽤 많아 보입니다. 그런데 문서를 읽으며 필자의 생각을 바꾼 지점이 있었습니다. 불편의 크기는 무엇과 비교하느냐에 따라 달라진다는 점입니다.
Claude Code 샌드박스에는 auto-allow 모드가 있습니다. 샌드박스 안에서 도는 명령은 묻지 않고 실행합니다. 수동 승인 모드와 비교하면 샌드박스는 오히려 프롬프트를 줄여 줍니다. 경계가 생겼으니 매번 물을 필요가 없어진 것입니다. 불편이 늘어나는 것은 bypass 모드와 비교할 때뿐입니다. 필자가 느낀 불편은 처음부터 “아무것도 막지 않는 상태"를 기준으로 한 것이었습니다.
반대로 bypass 모드를 유지한 채 샌드박스를 켜면 주의할 점이 있습니다. 문서에 따르면 bypass 모드에서는 샌드박스 안에서 실패한 명령을 샌드박스 밖에서 다시 실행하는 재시도가 묻지 않고 실행되고, 허용 목록에 없는 호스트로 가는 연결도 묻지 않고 허용됩니다. 경계를 켜 놓고 문을 열어 두는 셈입니다. bypass를 계속 쓴다면 이 두 출구를 사용자 설정에서 닫아야 합니다.
나머지 불편은 대부분 설정으로 줄일 수 있습니다. 문서가 제시하는 방법을 정리하면 다음과 같습니다.
- 새 호스트마다 막힘: 자주 쓰는 도메인을
allowedDomains에 미리 등록합니다. 다만github.com처럼 넓은 도메인은 그 자체로 유출 경로가 될 수 있습니다. 기본 프록시는 TLS(전송 구간 암호화)로 감싼 내용을 들여다보지 않아서, 허용된 도메인으로 위장하는 우회도 가능하다고 문서가 경고합니다. - SSH git: 원격 저장소 주소를 HTTPS로 바꿉니다. SSH를 유지하려면
git push같은 명령만 샌드박스 밖으로 빼야 하는데, 그렇게 뺀 명령은 허용 목록과 상관없이 어느 호스트로든 나갈 수 있습니다. - localhost 개발 서버: macOS에서는
allowLocalBinding으로 열 수 있지만, 그러면 localhost에서 도는 다른 서비스 전부에 닿게 됩니다. gh,docker처럼 호환되지 않는 도구:excludedCommands에 좁은 패턴으로 넣어 샌드박스 밖에서 실행하고, 필요하면 실행마다 묻도록 ask 규칙을 함께 겁니다.- Apple Events:
allowAppleEvents를 켜면 샌드박스 안의 명령이 다른 앱을 샌드박스 없이 띄울 수 있게 되어 실행 격리가 사실상 사라집니다. 브라우저를 띄우는 일은 필자가 직접 하거나open명령만 예외로 빼는 편이 낫습니다. - 클립보드: 에이전트가 응답에 출력한 내용을
/copy명령으로 복사합니다. 이 명령은 샌드박스 밖의 Claude Code 프로세스가 실행합니다. - 자격증명:
credentials설정으로~/.ssh,~/.aws/credentials읽기를 막고, 환경변수 토큰은 앞에서 본mask로 가짜 값을 줍니다. 다만 macOS에서는 파일 마스킹 대신 차단으로 처리되고, 마스킹에 필요한 TLS 종료 기능은 아직 실험 단계입니다.
이 중 가장 조심해야 할 것은 excludedCommands입니다. 편하다고 넓게 쓰면 경계가 무의미해집니다. 문서는 특히 인터프리터나 작업 디렉터리 안의 스크립트를 예외로 넣지 말라고 경고합니다. 에이전트가 그 파일을 고쳐 쓴 뒤 샌드박스 밖에서 실행할 수 있기 때문입니다.
필자가 생각한 설정의 뼈대는 다음과 같습니다. 엄격한 허용 목록 같은 일부 설정은 저장소 설정에서는 무시되고 사용자 설정에서만 적용되므로, ~/.claude/settings.json에 둡니다.
{
"sandbox": {
"enabled": true,
"allowUnsandboxedCommands": false,
"network": {
"strictAllowlist": true,
"allowedDomains": ["www.googleapis.com", "registry.npmjs.org"]
},
"credentials": {
"files": [
{ "path": "~/.ssh", "mode": "deny" },
{ "path": "~/.aws/credentials", "mode": "deny" }
]
}
}
}허용 목록을 처음부터 완성할 수는 없습니다. MXC가 Learning 모드로 관찰한 뒤 강제 모드로 넘어가는 것처럼, 처음에는 엄격 모드 없이 켜서 어떤 호스트가 필요한지 프롬프트로 모으고, 목록이 안정되면 엄격 모드로 잠그는 순서가 현실적이라고 필자는 봅니다.
그래도 남는 불편이 있을 것입니다. 그것까지 없애려 하면 다시 경계를 여는 쪽으로 돌아가게 됩니다. 필자는 그 나머지를 경계의 값으로 받아들이기로 했습니다. 불편을 모두 감수하거나 모두 피하는 것이 아니라, 설계로 줄일 수 있는 불편과 경계의 값으로 낼 불편을 나누는 것이 이번 점검에서 얻은 기준입니다.
필자의 선택#
이 기준에 따라 작업 성격별로 격리 수준을 나눠 보려 합니다.
- 대화형 일상 작업 (글쓰기, 브리프 작성): Claude Code 샌드박스를 auto-allow 모드로 켜고, 미리 정리한 허용 목록과 샌드박스 밖 재시도 차단을 함께 씁니다. bypass 모드를 계속 쓰더라도 이 출구들은 닫아 둡니다.
- 외부에 영향을 주는 명령 (배포 등): 샌드박스 밖에서 필자가 직접 실행합니다.
- 무인 · 장시간 작업: Bash 샌드박스만으로는 MCP 서버와 훅이 밖에 남으므로, 프로세스 전체를 감싸는
@anthropic-ai/sandbox-runtime이나 Docker Sandboxes의 microVM을 씁니다. Docker Sandboxes가 내세우는 커널 분리 수준의 격리는 아직 Docker 측의 설명이라는 점을 감안해야 합니다. - MXC: Claude Code가 지원하게 되면, 도구마다 다른 설정을 정책 하나로 옮길 수 있을지 다시 평가합니다.
브리프에 적었던 “Seatbelt로 Claude Code 감싸기” 실험은 MXC를 기다리지 않아도 지금 할 수 있습니다. /sandbox가 Bash 명령을 Seatbelt로 감싸고, sandbox-runtime이 Claude Code 프로세스 전체를 같은 방식으로 감쌉니다. 실제로 켜고 일해 보며 겪은 불편은 다음 글에서 이 목록과 비교해 기록하겠습니다.
맺으며#
필자가 샌드박스를 껐을 때 저울에 올린 것은 “불편"과 “위험"이었습니다. 다시 따져 보니 저울 자체가 잘못돼 있었습니다. 불편의 상당 부분은 설정으로 줄일 수 있었고, 위험을 막는다고 믿었던 꼼꼼한 읽기는 실행 뒤의 검토였습니다. 경계를 세우고 나면 읽기의 역할도 달라집니다. 필자가 응답을 읽는 데 쓰는 주의력을 보안 감시가 아니라 결과물의 품질에 돌릴 수 있게 됩니다.
참고 자료#
- Microsoft — Microsoft Execution Containers: policy-driven containment for AI agents
- GitHub — Local sandboxing for GitHub Copilot now generally available
- Claude Code — Configure the sandboxed Bash tool
- Claude Code — Choose a sandbox environment
- anthropics / sandbox-runtime
- OpenAI — Codex sandboxing
- Simon Willison — The lethal trifecta for AI agents
- Docker — Docker Sandboxes: Run Claude Code and other coding agents unsupervised (but safely)
- 2026-10-11 AI 뉴스 브리프