Claude Code에서 sandbox.enabled: true만 켜두면 자격증명이 자동으로 보호될 거라고 생각하기 쉬운데, 사실 그렇지 않습니다. 샌드박스의 기본 읽기 정책은 "일부 디렉터리만 빼고 컴퓨터 전체를 읽을 수 있게 허용"하는 쪽으로 설계돼 있어서, ~/.aws/credentials나 ~/.ssh 같은 자격증명 파일은 손대지 않으면 그대로 노출됩니다. 이 글에서는 왜 이런 구조인지, 실제로 어떤 위험으로 이어질 수 있는지, 그리고 sandbox.credentials로 정확히 어떻게 막는지 정리했습니다.

샌드박스를 켜도 자격증명이 뚫려 있는 이유
Claude Code의 Bash 샌드박스는 매번 승인을 묻지 않고도 명령을 실행할 수 있게 해주는 격리 환경입니다. macOS는 Seatbelt, Linux·WSL2는 bubblewrap과 socat 조합으로 파일시스템·네트워크 경계를 강제하는 방식이죠. 근데 이 격리, 쓰기와 읽기를 다르게 취급해요.
공식 문서(code.claude.com/docs/en/sandboxing)는 기본 읽기 동작을 이렇게 명시합니다. "read access to the entire computer, except certain denied directories. Note that this default still allows reading credential files such as ~/.aws/credentials and ~/.ssh/." 쓰기는 현재 작업 디렉터리와 세션 임시 디렉터리로 좁혀지지만, 읽기는 기본적으로 열려 있다는 뜻입니다.
환경변수 쪽도 마찬가지입니다. 문서의 Scope 섹션은 "sandboxed Bash commands inherit the parent process environment by default, including any credentials set there"라고 밝힙니다. 셸에 GITHUB_TOKEN 같은 값이 export돼 있으면, 샌드박스 안에서 실행되는 명령도 그 값을 그대로 물려받습니다. 샌드박스가 막아주는 건 "쓰기"와 "새 도메인 네트워크 접근"이지, 이미 컴퓨터에 있는 자격증명 읽기가 아니라는 걸 정확히 짚고 넘어가야 합니다.
자격증명 노출이 실제로 문제가 됐던 사례
이게 왜 이론적인 얘기가 아니라 실전 문제인지 보여주는 사례가 있습니다. 블로그 Arrange Act Assert(2026년 6월 30일 게시)는 Mozilla 0DIN 팀이 보고한 2026년 6월 사례를 소개하는데, 겉보기엔 평범한 GitHub 저장소가 AI 코딩 에이전트를 리버스 셸로 유도한 케이스입니다. 저장소 자체에는 악성 코드가 전혀 없고, 정상적인 Python 패키지 설치가 실패한 뒤 공격자가 제어하는 DNS TXT 레코드를 조회해서 그 안의 페이로드를 bash로 파이핑해 실행하는 방식이었다고 합니다. 페이로드가 실행 시점에 원격에서 가져와지니 코드 리뷰로는 잡히지 않는 구조죠.
이 글의 저자는 "The sandbox limits writes well by default. It does not limit reads the same way"라고 못박습니다. 설정하지 않으면 기본적으로 열려 있는 항목으로 ~/.ssh, ~/.aws/credentials, npm·GitHub 토큰, 클라우드 제공자 구성 파일을 꼽았습니다.
여기서 하나 구분해서 짚을 부분이 있습니다. cybersecuritynews.com이 보도한 또 다른 취약점은 성격이 좀 다릅니다. 2025년 10월(v2.0.24)부터 2026년 4월(v2.1.90) 패치까지 약 5.5개월간 존재했던 네트워크 샌드박스 우회 취약점(CVE-2025-66479 계열)인데, allowedDomains: []로 모든 아웃바운드를 막으려던 설정이 결함 있는 체크 로직 때문에 오히려 "전부 허용"으로 오작동한 것과, SOCKS5 호스트명에 널바이트를 심어 필터를 속이는 방식(attacker-host.com\x00.google.com처럼 JS 필터는 뒤쪽 도메인만 보고 통과시키지만 getaddrinfo()는 널바이트에서 문자열을 끊어 실제로는 앞쪽 공격자 도메인으로 연결)이 결합된 사고였습니다. 이건 사용자가 설정을 잘못한 게 아니라 구현 자체의 버그였습니다.
두 사례를 나란히 놓으면 왜 다층 방어가 필요한지 드러납니다. Mozilla 사례는 "기본 정책이 원래 이렇게 설계됐다"는 문제고, CVE 사례는 "의도한 정책조차 버그로 뚫릴 수 있다"는 문제입니다. sandbox.credentials로 자격증명을 명시적으로 차단해두면, 설계상의 허점이든 예상 못한 구현 버그든 최소한 자격증명 자체는 애초에 프로세스에 노출되지 않도록 막는 역할을 합니다. 공식 문서 Limitations 섹션도 "Sandboxing reduces risk but is not a complete isolation boundary"라고 인정하는 부분입니다.

sandbox.credentials 실전 설정법
그럼 실제로는 어떻게 설정해야 할까요? sandbox.credentials는 Claude Code v2.1.187부터 추가된 전용 설정 블록입니다. files와 envVars 두 배열로 구성되고, 각 항목은 경로(또는 변수명)와 mode를 가집니다.
{
"sandbox": {
"enabled": true,
"credentials": {
"files": [
{ "path": "~/.aws/credentials", "mode": "deny" },
{ "path": "~/.ssh", "mode": "deny" }
],
"envVars": [
{ "name": "GITHUB_TOKEN", "mode": "deny" },
{ "name": "NPM_TOKEN", "mode": "deny" }
]
}
}
}
files 항목은 "mode": "deny"만 지원합니다. 샌드박스 내에서 해당 경로 읽기가 완전히 거부되는 방식이고, 사실상 filesystem.denyRead와 같은 메커니즘입니다. 공식 문서는 이 자격증명 전용 블록을 따로 둔 이유를 "keeps credential rules grouped together and separate from general filesystem rules"라고 설명합니다 — 일반 파일 규칙과 섞이지 않게 구분해두려는 목적입니다.
envVars는 한 단계 더 나아가서 "mode": "mask"(v2.1.199+)를 지원합니다. deny는 변수를 아예 unset 처리해버려서 그 값으로 인증하던 gh, npm 같은 도구가 깨질 수 있는데, mask는 샌드박스 안 명령에는 세션별 sentinel 값만 보여주고, 요청이 샌드박스 밖으로 나가 지정된 injectHosts로 향할 때 프록시가 실제 값으로 바꿔치기합니다.
{
"sandbox": {
"enabled": true,
"network": {
"tlsTerminate": {},
"allowedDomains": ["*.github.com", "registry.npmjs.org"]
},
"credentials": {
"envVars": [
{ "name": "GH_TOKEN", "mode": "mask", "injectHosts": ["api.github.com"] },
{ "name": "NPM_TOKEN", "mode": "mask" }
]
}
}
}

주의할 점 하나. mask가 동작하려면 network.tlsTerminate를 반드시 같이 설정해서 프록시가 TLS를 직접 종료해야 합니다. 이걸 빼먹으면 마스킹이 fail closed 상태가 됩니다 — 명령은 여전히 sentinel만 보지만, 그 sentinel이 그대로 서버로 전달돼 인증 자체가 실패하는 거죠. 다행히 Claude Code가 시작 시점에 이 설정 오류를 알려줍니다.
공식 문서가 한 문장으로 정리하는 원칙도 기억해둘 만합니다. "There is no built-in credential deny list, so only the files and variables you list are restricted." 자동으로 보호되는 기본 목록 같은 건 없고, 직접 나열한 것만 막힙니다.
deny와 mask, 어디에 써야 통하는지
설정 스코프 얘기를 빼놓을 수 없습니다. Claude Code 설정은 Managed → Command line → Local(.claude/settings.local.json) → Project(.claude/settings.json) → User(~/.claude/settings.json) 순으로 우선순위가 매겨지는데, deny 계열 배열 값은 이 순서와 무관하게 모든 스코프에서 병합됩니다. 즉 프로젝트 설정에 자격증명 차단을 걸어두면 사용자가 로컬 설정으로 이를 풀 수 없다는 뜻입니다.
반대로 mask 항목과 network.tlsTerminate, credentials.allowPlaintextInject는 저장소에 포함되는 .claude/settings.json이나 .claude/settings.local.json에서는 아예 무시됩니다. 사용자 설정, 관리형 설정, --settings CLI 플래그처럼 신뢰도 높은 스코프에서만 적용됩니다. 저장소 설정 파일은 협업자나 공격자가 손댈 여지가 있으니, 자격증명을 실제 값으로 주입하는 위험한 동작은 신뢰된 스코프에서만 열어준다는 설계로 보입니다.
이 우선순위 구조는 Claude Code 매번 승인 물어볼 때 — Manual 모드 정체와 Auto 복원법에서 다룬 권한 흐름과도 맞닿아 있습니다. 샌드박스가 Auto-allow 모드로 동작할 때는 승인 절차 자체가 생략되는 만큼, 파일·환경변수 차단 규칙이 스코프별로 정확히 어디서 먹히는지 미리 알아두는 게 중요합니다.
조직 전체에 강제하려면 관리형 설정에 아래처럼 배포하고, sandbox.credentials 항목을 추가하라고 공식 문서도 권고합니다.
{
"sandbox": {
"enabled": true,
"failIfUnavailable": true,
"allowUnsandboxedCommands": false
}
}
문서 원문: "Add sandbox.credentials entries for credential directories such as ~/.aws and ~/.ssh and for secret environment variables, since the default read policy still allows them." allowManagedReadPathsOnly, allowManagedDomainsOnly 같은 잠금 옵션을 걸어두면 개발자가 로컬에서 정책을 넓히는 것도 막을 수 있습니다. 다만 excludedCommands에는 이런 잠금 대응 옵션이 없어서, 여기 등재된 명령은 샌드박스 제약을 아예 우회한다는 점도 기억해두는 게 좋습니다.
문서에 없었다는 얘기, 지금은 사정이 다릅니다
sandbox.credentials가 v2.1.187에서 조용히 추가되고 나서, 한동안 공식 문서에 이 설정이 전혀 반영되지 않았던 시기가 있었습니다. 2026년 6월 23일 올라온 GitHub 이슈 #70440이 이 문제를 짚었는데, 샌드박싱 가이드가 여전히 옛날 방식인 수동 denyRead 설정만 안내하고 있었고, 설정 페이지의 sandbox 설정 표에도 이 항목이 빠져 있었다는 지적이었습니다.
근데 이 리서치를 진행한 시점 기준으로 공식 문서를 직접 확인해보니, 이미 "Protect credentials"라는 전용 섹션이 생겨서 스키마와 예시, mask 모드까지 상세히 서술돼 있었습니다. 이슈 제기 이후 문서가 보강된 것으로 보입니다. 그러니 지금 이 설정을 검색하면서 "공식 문서에 안 나온다"는 식으로 알고 있었다면, 그건 이미 지나간 얘기라는 것만 짚고 넘어가겠습니다.
FAQ
Q. sandbox.enabled만 켜도 자격증명이 안전한가요?
아니요. 기본 읽기 정책이 컴퓨터 전체를 허용하는 쪽이라 ~/.aws/credentials, ~/.ssh 같은 파일은 sandbox.credentials.files에 직접 등록해야 차단됩니다.
Q. deny와 mask 중 뭘 써야 하나요?gh, npm처럼 그 자격증명으로 인증해서 계속 동작해야 하는 도구가 있으면 mask, 아예 접근 자체를 막아도 되는 경우면 deny가 맞습니다. 단 mask는 network.tlsTerminate 설정과 신뢰된 스코프(사용자/관리형 설정)가 필요합니다.
Q. 프로젝트 저장소의 .claude/settings.json에 mask를 써도 되나요?
공식 문서 기준으로는 무시됩니다. mask, network.tlsTerminate, credentials.allowPlaintextInject는 사용자 설정, 관리형 설정, --settings CLI 플래그에서만 유효합니다.
권한 프롬프트나 승인 흐름이 헷갈렸다면 Claude Code 매번 승인 물어볼 때 — Manual 모드 정체와 Auto 복원법을, 파일 접근 권한 자체에서 막힌 적이 있다면 Claude Code EACCES 오류 해결법, npm 전역 설치 실패 체크리스트를 같이 보면 설정 맥락이 좀 더 잡힙니다. 샌드박스 설정을 걸어두고 나서 컨텍스트 압축 관련 이슈가 궁금하다면 Autocompact is thrashing 해결법 — Claude Code 컨텍스트 압축 루프 대처도 참고할 만합니다. 아직 sandbox.credentials를 설정 안 해뒀다면, 지금 쓰고 있는 .aws, .ssh 경로부터 하나씩 등록해보는 걸 권합니다.
'AI활용' 카테고리의 다른 글
| HWP-MCP 완벽 가이드 2026, Claude로 한글 문서 자동화하기 (0) | 2026.07.06 |
|---|---|
| Claude Code Plan Mode 완벽 활용법: 설계부터 실행까지 (0) | 2026.07.05 |
| Claude Code Task Budgets, 왜 안 될까 — API 비용 폭주 진짜로 막는 법 (2026) (0) | 2026.07.05 |
| Claude Code ultrareview 써보니, 아무 커밋에나 쓸 건 아니었다 (0) | 2026.07.05 |
| AGENTS.md 완전 가이드: AI 코딩 도구 28개가 함께 읽는 단 하나의 파일 (1) | 2026.07.05 |
