Skip to content

Claude Code 권한 강화: 저장소 밖 쓰기 차단, 프롬프트 대신 거부, settings.local.json 점검

업데이트

Claude Code 2.1.319에서 직접 테스트한 settings.json: blockWritesOutsideWorkingDirectories, denyInsteadOfPrompting, Read deny 규칙, 무인 실행용 dontAsk 모드, 그리고 settings.local.json 유출 점검과 백도어 확장 확인.

요약: 아래 내용을 저장소 루트의 .claude/settings.json에 넣고 커밋한 뒤 Claude Code를 재시작하면 됩니다. Claude Code 2.1.319 이상이 필요합니다 (claude --version으로 확인하고 claude update로 업데이트).

{
  "permissions": {
    "blockWritesOutsideWorkingDirectories": true,
    "denyInsteadOfPrompting": true,
    "deny": [
      "Read(./.env)",
      "Read(./.env.*)",
      "Read(./secrets/**)",
      "Bash(curl *)",
      "Bash(wget *)"
    ]
  }
}

각 줄이 해 주는 일:

항목역할추가된 버전
blockWritesOutsideWorkingDirectoriesWrite, Edit, NotebookEdit 도구가 작업 디렉터리 밖의 모든 경로를 거부합니다. bypassPermissions를 포함한 모든 권한 모드에서 적용됩니다2.1.314 (2026년 10월 30일)
denyInsteadOfPrompting권한 프롬프트를 기다려야 하는 도구 호출을 거부하고 턴을 계속 진행합니다. 터미널 세션, 서브에이전트, 팀메이트에 적용됩니다2.1.319 (2026년 11월 6일)
Read(...) deny 규칙.env와 secrets/는 파일 도구에서도, 파일 이름을 지정하는 cat, head, sed 같은 셸 명령에서도 읽을 수 없습니다예전부터 있던 기능
Bash(curl *) / Bash(wget *)셸에서의 네트워크 요청을 묻지 않고 바로 거부합니다예전부터 있던 기능

쓰기 차단, deny 규칙, 무인 실행 동작은 2026년 11월 8일 macOS에서 Claude Code 2.1.319로, 테스트마다 권한 모드를 지정한 claude -p --output-format json 실행으로 직접 테스트했습니다. 정확한 거부 메시지는 아래에 그대로 인용했습니다. 체인지로그는 denyInsteadOfPrompting을 터미널 세션, 서브에이전트, 팀메이트에 적용되는 설정으로 설명하는데, -p 실행은 이미 그런 호출을 거부하기 때문에 이 설정에 대한 설명은 테스트가 아니라 체인지로그 (opens in a new tab)를 근거로 합니다. 이 가이드의 나머지 부분에서는 이 설정들이 지금 왜 중요한지, 각 설정이 무엇을 막고 무엇을 막지 못하는지, 그리고 한 번은 꼭 실행해야 할 세 가지 점검을 설명합니다.

왜 지금인가: 9일 동안 세 건의 보안 사건

Claude Code는 2026년 10월 26일부터 11월 6일 사이에 일곱 번 릴리스를 냈고, 같은 기간에 보안 이슈 세 건이 터졌습니다. 각 사건은 구체적인 점검 항목 하나로 이어집니다.

날짜무슨 일이 있었나한 번만 하면 되는 점검
10월 30일Phorion이 Cursor Marketplace 배포 채널에서 공식 Claude Code for VS Code 확장의 백도어 빌드(버전 2.1.312, 2.1.313)를 발견했습니다. 공격자의 SSH 키를 ~/.ssh/authorized_keys에 추가하고 영구 실행(persistence) 장치를 설치합니다 (권고문 (opens in a new tab), HN 1,011 포인트 (opens in a new tab))확장 버전을 확인하고 authorized_keys를 grep 하세요. 아래 5단계 참고
11월 5일GitHub 이슈 #107757 (opens in a new tab): Claude Code가 웹 요청의 User-Agent 헤더에 계정 이메일 주소를 넣어 보냈습니다. 반응 약 1,000개, 11월 8일 기준 아직 열려 있습니다 (HN 1,597 포인트 (opens in a new tab))도구가 이메일을 보내기 전에 확인을 요청하는 2.1.317 이상으로 업데이트하세요. 아래 6단계 참고
11월 7일Apiiro가 "Yes, and don't ask again" 승인으로 비밀번호나 토큰이 담긴 셸 명령이 .claude/settings.local.json에 저장된 공개 GitHub 파일 1,122개를 발견했습니다 (보고서 (opens in a new tab), HN 500+ 포인트 (opens in a new tab))아래 4단계의 점검 스크립트를 실행하세요

이 중 어느 것도 권한 모드만으로는 해결되지 않습니다. 설정 파일, 점검, 버전 확인이 필요합니다.

인기 글

1단계: 저장소 밖 파일 쓰기 차단

permissions.blockWritesOutsideWorkingDirectories를 켜면 파일 편집 도구 세 개가 작업 디렉터리 안에 없는 모든 경로를 거부합니다. 작업 디렉터리는 Claude Code를 시작한 폴더에 /add-dir, --add-dir, 또는 사용자 설정이나 관리형(managed) 설정의 permissions.additionalDirectories로 추가한 디렉터리를 더한 것입니다. 저장소 자체의 .claude/settings.json에 넣은 additionalDirectories 항목은 이 차단을 풀지 못합니다. 이 차단은 bypassPermissions를 포함한 모든 권한 모드에서 유지되고, 어느 설정 파일에든 true가 하나라도 있으면 다른 곳의 false보다 우선하므로, 저장소 차원에서 켜 두면 클론하는 모든 사람에게 적용됩니다.

테스트용 프로젝트에 이 키를 설정하고, acceptEdits 모드에서 Claude Code에 Write 도구로 홈 디렉터리에 파일을 만들어 달라고 요청했습니다. 호출은 다음 메시지와 함께 거부되었습니다:

/Users/me/kanaries-block-test.txt is outside /path/to/proj; the
permissions.blockWritesOutsideWorkingDirectories setting blocks writes
outside the working directories. Ask the user to add the directory with
/add-dir, or to remove that setting.

파일은 생성되지 않았고, JSON 결과의 permission_denials에 해당 호출이 tool_name: "Write"로 기록되었습니다.

의존하기 전에 알아야 할 네 가지 한계:

  • 셸 명령, MCP 도구, 훅 프로그램은 제한하지 않습니다. Bash 도구에서 실행한 echo x > ~/file은 Write 호출이 아닙니다. 셸 쓰기까지 막으려면 샌드박스를 켜야 합니다 (아래 "선택 사항: 셸도 샌드박스로 격리" 참고).
  • Claude Code 자체 파일 일부는 계속 쓸 수 있습니다: 플랜 파일, 세션 스크래치패드, 백그라운드 세션의 tmp 디렉터리, 프로젝트의 자동 메모리 .md 파일이 그렇습니다. 의도된 동작입니다.
  • Claude가 더 이상 Claude Code 설정 파일을 편집할 수 없습니다. 저장소 안의 설정 파일이라도 마찬가지인데, 설정 파일이 이 차단을 풀 수 있기 때문입니다. .claude/settings.json은 직접 편집하세요.
  • Worktree 도구가 사라집니다. 이 키를 켜면 Claude Code가 EnterWorktree와 ExitWorktree를 제거하거나 해당 호출을 거부합니다. 서브에이전트에 worktree가 필요하다면 저장소 루트의 .claude/worktrees를 작업 디렉터리로 추가하세요.

짝이 되는 설정인 permissions.blockReadsOutsideWorkingDirectories(2.1.257 이상)는 Read, Grep, Glob, LSP 호출에 같은 역할을 합니다. 저장소가 디스크의 나머지 부분을 보면 안 될 때 켜 두세요.

2단계: deny 규칙으로 시크릿 읽기 차단

Read(path) deny 규칙은 내장 파일 도구를 차단하고, 문서에 따르면 Claude Code가 Bash에서 인식하는 파일 명령(cat, head, tail, sed, tee)과 해당 파일을 지정하는 셸 리다이렉션도 차단합니다. 같은 경로에 대한 Edit과 Write도 막기 때문에 Claude가 .env를 덮어쓸 수도 없습니다.

테스트 결과: "deny": ["Read(./.env)"]를 설정한 상태에서 Read 도구로 .env를 읽어 달라고 하면 다음이 반환되었습니다:

File is in a directory that is denied by your permission settings.

이 규칙은 셸까지 적용됩니다. acceptEdits 실행에서 cat README.md를 요청한 뒤 cat .env를 요청했는데, 첫 번째는 파일 내용을 출력했고 두 번째는 Permission to use Bash with command cat .env has been denied. 메시지와 함께 거부되었습니다. 별도의 기본 모드 claude -p 실행에서는 Bash(curl *) 규칙 때문에 curl -sI https://example.com이 거부되었습니다. 모든 거부는 permission_denials에 기록되었고, 각 실행은 멈추지 않고 정상 종료되었습니다.

권한 레퍼런스 (opens in a new tab)에 나오는 규칙 문법의 함정 세 가지:

  • Read(...)와 Edit(...)을 사용하세요. 경로를 지정한 Write(docs/**), Grep(docs/**), Glob(...) 규칙은 받아들여지지만 절대 매칭되지 않습니다.
  • * 앞에 공백이 있는 Bash(git diff *)는 git diff로 시작하는 모든 명령에 매칭됩니다. Bash(git diff*)는 git diff-index에도 매칭됩니다.
  • deny 규칙은 시크릿이 있는 폴더에서 실행한 grep -r pattern .처럼 파일 이름을 지정하지 않고 읽는 명령은 잡지 못합니다. 그런 경우를 위한 것이 샌드박스입니다.

.claudeignore 파일은 아무 효과가 없습니다. 그 항목들을 Read deny 규칙으로 옮기세요.

3단계: 권한 프롬프트에서 멈추지 않게 만들기

무인 Claude Code 실행이 기다리는 대신 거부하도록 만드는 방법은 이제 네 가지입니다. 실행 환경에 따라 고르세요.

상황사용할 것동작
자리를 비우는 대화형 터미널 세션 (서브에이전트와 팀메이트 포함)설정에 "denyInsteadOfPrompting": true (2.1.319)프롬프트를 기다려야 하는 호출이 거부되고 턴이 계속 진행됩니다 (체인지로그 표현. -p 모드는 이미 거부하기 때문에 -p에서는 테스트할 수 없었습니다)
권한 모드를 지정하지 않은 claude -p 또는 Agent SDK 실행추가 설정 없음2.1.302부터 -p는 Manual 모드로 시작하며 승인이 필요한 호출을 모두 거부합니다. 위에서 테스트했습니다
잠겨 있는 CI 작업--permission-mode dontAsk프롬프트가 뜰 모든 호출이 거부됩니다 (설치한 mod가 일부 호출을 승인할 수는 있습니다). 읽기 전용 명령, 작업 디렉터리 안의 파일 읽기, allow 규칙은 계속 실행됩니다
canUseTool을 쓰는 SDK 앱이나 --permission-prompt-tool 같은 권한 호스트가 있는 실행--permission-prompts none (2.1.259 이상)호스트를 기다리지 않습니다. Claude에게는 승인할 사람이 없으니 재시도하지 말라고 알려 줍니다

--permission-mode dontAsk --permission-prompts none으로 테스트했습니다: python3 -c 'print(2+2)'는 Permission to use Bash has been denied because Claude Code is running in don't ask mode로 시작하는 메시지와 함께 거부되었고, git log --oneline -1은 내장 읽기 전용 명령 목록에 있어서 실행되었습니다. 결과 JSON의 permission_denials에는 항목 하나가 기록되었습니다.

bypassPermissions를 포함해 어떤 모드나 allow 규칙으로도 승인할 수 없는 것들: 명시적인 ask 규칙, AskUserQuestion 도구, requiresUserInteraction으로 표시된 MCP 도구, rm -rf ~ 같은 중요 경로를 인식한 rm 명령. dontAsk는 이것들을 모두 바로 거부하고, allow 규칙이 매칭되더라도 네트워크 경로 읽기까지 거부합니다.

저장소를 절대 bypass 모드로 실행하면 안 된다면 permissions 아래에 "disableBypassPermissionsMode": "disable"을 추가하세요. 그러면 Claude Code가 --dangerously-skip-permissions를 거부하고, bypass를 요청하는 서브에이전트 정의도 무시합니다.

4단계: settings.local.json에서 유출된 allow 규칙 점검

Apiiro 보고서 뒤에 있는 메커니즘은 이렇습니다. Claude가 셸 명령 실행 권한을 요청할 때 Yes, and don't ask again을 선택하면, Claude Code는 해당 명령 접두사를 가진 allow 규칙을 저장소 루트의 .claude/settings.local.json에 저장합니다. 그 명령이 psql postgres://app:Sup3rSecret@db/prod -c ...였다면 비밀번호가 이제 규칙 안에 들어 있습니다.

Claude Code는 이 파일을 아직 무시하지 않는 저장소에 처음 파일을 쓸 때 전역 git excludes 파일에 **/.claude/settings.local.json을 추가합니다. 제 Mac에서는 그 파일이 ~/.config/git/ignore이고 패턴은 1번 줄에 있습니다. 그래서 여기서는 git add .claude/settings.local.json이 "ignored by one of your .gitignore files" 오류로 실패합니다. 하지만 이 보호는 저장소 단위가 아니라 머신 단위입니다. Claude Code가 쓰기 전에 직접 만든 파일, 클론이 아니라 디렉터리 복사로 다른 머신에 옮긴 저장소, 전역 excludes가 없는 샌드박스에서는 이 파일이 다른 파일처럼 그대로 커밋됩니다. Apiiro 저자는 HN에서, 추적한 저장소 1,105개 중 539개에서 Claude가 공동 작성자인 커밋이 이 파일을 변경했다고 밝혔습니다.

저장소 루트에서 아래를 실행하세요. 모든 .claude/settings*.json을 찾아 git이 추적 중인지 보고하고, 자격 증명이 담겼거나 도구 전체를 통째로 허용하는 allow 규칙에 표시를 붙입니다.

cat > /tmp/claude-settings-audit.sh <<'EOF'
#!/usr/bin/env bash
set -u
root="${1:-.}"
found=0
while IFS= read -r -d '' f; do
  found=1
  echo "== $f"
  if git -C "$(dirname "$f")" ls-files --error-unmatch "$(basename "$f")" >/dev/null 2>&1; then
    echo "   TRACKED BY GIT  <- settings.local.json should never be"
  fi
  grep -nE '"(Bash|PowerShell)\([^"]*(password|passwd|token|secret|api[_-]?key|Authorization|Bearer |PGPASSWORD|AWS_SECRET|--password|://[^"/]*:[^"@]*@|[A-Z_]{3,}=[^[:space:]"]{8,})' "$f" \
    | sed 's/^/   SECRET-LIKE: /'
  grep -nE '"(Bash|PowerShell)(\(\*\))?"|"Bash\((sudo|rm|curl|wget|ssh|scp|git push|npm publish|docker)[^"]*\*\)"' "$f" \
    | sed 's/^/   BROAD: /'
done < <(find "$root" -path '*/node_modules' -prune -o -type f \
  \( -name 'settings.local.json' -o -name 'settings.json' \) -path '*/.claude/*' -print0 2>/dev/null)
[ "$found" = 0 ] && echo "No .claude/settings*.json under $root"
EOF
bash /tmp/claude-settings-audit.sh .

규칙 여섯 개가 들어 있는 합성 파일에서 데이터베이스 비밀번호, bearer 토큰, DATABASE_URL=... 할당이 들어간 세 개와 Bash(*), Bash(rm -rf *)를 표시했고, curl 규칙은 BROAD 항목으로 한 번 더 표시했습니다. 테스트 저장소에서 이 파일을 커밋한 상태에서는 TRACKED BY GIT 줄도 출력했습니다. grep이기 때문에 결과는 직접 읽고 판단해야 합니다.

표시된 항목이 나오면:

  1. 자격 증명을 먼저 교체(rotate)하세요. 파일이 한 번이라도 푸시됐다면 그 시크릿은 공개된 것으로 간주하세요. 포크나 스크래퍼가 이미 가져갔다면 히스토리를 다시 쓰는 것만으로는 충분하지 않습니다.
  2. 규칙을 삭제하거나, 시크릿을 담지 않는 규칙으로 바꾸세요: psql *를 승인하고 비밀번호는 명령 안이 아니라 셸 환경의 PGPASSWORD로 전달하세요.
  3. git이 파일을 추적 중이라면 git rm --cached .claude/settings.local.json을 실행하고 저장소의 .gitignore에 .claude/settings.local.json을 추가하세요. 그러면 보호가 각 머신의 전역 excludes에 의존하지 않고 저장소와 함께 이동합니다.
  4. .claude/settings.json도 확인하세요. 이 파일은 커밋하라고 만든 것이므로, 여기에 시크릿이 담긴 allow 규칙이 있다면 사고가 아니라 설계상 유출입니다.

같은 이유로, 커밋하는 파일에는 deny와 ask 규칙을 우선 넣고 개인 allow 규칙은 로컬에 두세요. deny와 ask 규칙은 즉시 적용되지만, 저장소에서 온 allow 규칙은 팀원 각자가 해당 폴더를 신뢰(trust)한 뒤에만 적용됩니다.

5단계: IDE 확장과 SSH 키 확인

백도어가 심긴 빌드는 Cursor Marketplace를 통해 배포된 Claude Code for VS Code 확장의 2.1.312 및 2.1.313 버전으로, Phorion이 10월 30일에 보고했습니다. Cursor는 해당 버전을 삭제했습니다. Phorion에 따르면 페이로드는 확장 모듈이 로드되는 즉시 실행되어 svc-telemetry 주석이 붙은 Ed25519 키를 ~/.ssh/authorized_keys에 추가하고, 원격 로그인(Remote Login)을 켜려고 시도하며, 호스트 정보를 외부로 전송합니다. macOS에서는 임플란트의 버그 때문에 관리자가 아닌 사용자에게는 SSH 활성화 단계가 실패하지만, 키와 영구 실행 장치는 그대로 남습니다.

설치된 버전 확인:

cursor --list-extensions --show-versions | grep -i claude
code --list-extensions --show-versions | grep -i claude
ls ~/.cursor/extensions ~/.vscode/extensions 2>/dev/null | grep -i claude

제 머신에서는 anthropic.claude-code@2.1.302가 나왔고, 영향받는 범위 밖입니다. 다음으로 Phorion이 공개한 지표를 확인하세요:

grep -n "svc-telemetry" ~/.ssh/authorized_keys 2>/dev/null || echo "no match"

Phorion은 이 주석 문자열이 공격자가 정하는 값이라서 grep 결과가 깨끗해도 증명은 아니라고 지적합니다. Cursor Marketplace에서 2.1.312나 2.1.313을 실행한 적이 있다면 authorized_keys에서 모르는 키를 전부 제거하고, 로그인 항목과 launch agent를 검토하고, 사고 대응 검토가 끝날 때까지 그 머신을 침해된 것으로 취급하세요. 확장은 VS Code Marketplace에서 설치하거나 claude CLI가 /ide로 설치하도록 하세요. 2.1.318부터 Claude Code는 IDE 터미널 안에서 확장을 스스로 설치하거나 업데이트하지 않습니다.

6단계: User-Agent 이메일 노출 수정

이슈 #107757은 Claude Code가 검색과 fetch 중에 보내는 웹 요청의 User-Agent 헤더에 계정 이메일 주소를 넣어, 사이트와 트래커가 이를 수집할 수 있었다고 보고합니다. 11월 3일자 2.1.317 체인지로그 항목은 이렇게 적혀 있습니다: "Added a check that asks before a web or shell tool call sends your account email address to the network." 제보자는 2.1.318에서 이슈를 올렸고, 11월 8일 기준 반응 약 1,000개와 함께 아직 열려 있으므로 최종 해결까지 지켜봐야 합니다.

지금 할 수 있는 일: 업데이트(claude update)하세요. 무인 실행을 한다면, denyInsteadOfPrompting이나 dontAsk에서는 이 새로운 "확인 요청" 검사가 거부로 바뀐다는 점을 알아 두세요. 그게 바로 원하는 동작입니다.

각 가드가 막는 것: 테스트됨 vs 문서 기준

시도blockWrites…Read deny 규칙Bash(curl *) denydontAsk / -p 기본값샌드박스
Write 도구로 ~/x.txt 생성거부 (테스트)——프롬프트 → 거부샌드박스는 허용. 파일 도구는 샌드박스 밖
Read 도구로 .env 읽기—거부 (테스트)—허용. 읽기는 승인 불필요—
Bash에서 cat .env—거부 (테스트)—거부—
Bash에서 echo x > ~/x.txt해당 없음——프롬프트 → 거부차단 (문서: macOS에서 Operation not permitted)
Bash에서 curl https://…——거부 (테스트)거부허용된 호스트가 아니면 차단
Bash에서 python3 -c …———거부 (테스트)샌드박스 안에서 실행
git log, ls 등 읽기 전용 명령———실행 (테스트)실행
rm -rf ~———거부. 어떤 규칙으로도 승인 불가차단. 홈은 쓰기 가능 범위 밖 (문서)

"테스트"는 2026년 11월 8일에 2.1.319에서 제가 직접 실행했다는 뜻입니다. 나머지는 모두 이 가이드에 링크한 공식 문서를 근거로 합니다.

선택 사항: 셸도 샌드박스로 격리

위 설정은 Claude 자체의 파일 도구를 막습니다. 셸 명령은 기본적으로 꺼져 있는 Bash 샌드박스 (opens in a new tab)가 막습니다. macOS Seatbelt를 사용하며, Linux나 WSL2에서는 bubblewrap과 socat이 필요합니다. 프로젝트 하나에서만 켜려면 세션 안에서 /sandbox를 실행하고, 모든 프로젝트에서 켜려면 ~/.claude/settings.json에 다음을 추가하세요:

{
  "sandbox": {
    "enabled": true
  }
}

작동 확인은 Claude에게 touch ~/sandbox-probe를 실행해 달라고 요청하면 됩니다. 샌드박스 안에서는 macOS에서 Operation not permitted, Linux에서 Read-only file system으로 실패합니다. Claude가 샌드박스 밖에서 명령을 재시도하겠다고 제안하면 거절하세요.

위 매트릭스의 빈틈은 이 조합으로 메울 수 있습니다: 파일 도구는 blockWritesOutsideWorkingDirectories로, 셸은 샌드박스로, 시크릿은 Read deny 규칙으로, 무인 실행은 denyInsteadOfPrompting으로 각각 막습니다. 그러면 bypass 모드는 남아 있는 프롬프트를 빨리 넘기는 역할만 합니다. 격리된 작업에 여전히 bypass가 필요하다면 bypass permissions 가이드에서 토글과 오류 메시지를 다룹니다.

FAQ

관련 가이드