이 페이지의 목차
이 페이지의 목차
"이 명령은 막는다", "이 파일은 못 고친다" 같은 정책을
PreToolUse등 훅으로 강제할 때, 훅이 조용히 새는 경우를 막는 점검 순서다. 근거는 공식 Hooks reference Claude Code 훅 레퍼런스와 가이드 Claude Code 훅 가이드(둘 다 원본 날짜 미상, 수집 2026-10-18)다. 버전 조건은 수집일 기준이다. 아래 항목은 훅을 "더 믿을 만하게" 만드는 방법이지, 훅만으로 모든 우회를 막는다는 보장이 아니다. 원본도 확실한 허용·거부는 권한 시스템으로 하라고 한다.
언제 쓰고 언제 쓰지 않는가#
- 쓴다: 고객사·팀에 "Claude Code가 이건 못 한다"를 약속해야 할 때. 예: 운영 DB 명령 차단, 보호 파일 편집 차단, 특정 모델 전환 차단, 프롬프트 검사.
- 훅보다 먼저 고려할 것: 도구·경로 단위의 확실한 허용·거부. 원본: "Because the
iffilter is best-effort, use the permission system rather than a hook to enforce a hard allow or deny." → 권한 모드, Claude Code 관리자 권한 통제Claude Code 훅 레퍼런스 - 쓰지 않는다: 비밀을 디스크에서 지키는 수단으로. 막힌 프롬프트도 트랜스크립트·프롬프트 기록에 남을 수 있다(아래 7단계). Claude Code 훅 레퍼런스
1단계: "막는다"를 제대로 신호한다#
- 막으려면 종료 코드 2(종료 코드는 용어 사전)나 종료 코드 0 + JSON 결정(
permissionDecision: "deny"등)을 쓴다. 종료 코드 1은 올바른 JSON 없이 끝나면 막지 않는 오류라 동작이 진행된다(실패하면 막도록 설정한 훅, 즉onFailure: "block"은 예외). 원본: "If your hook is meant to enforce a policy, useexit 2." Claude Code 훅 레퍼런스 - 조용히 끝나는 것은 승인이 아니다. 종료 코드 0에 출력이 없으면 일반 권한 흐름으로 간다. Claude Code 훅 레퍼런스
- 한 훅에서는 종료 코드 방식과 JSON 방식 중 하나만 쓴다. 섞으면 종료 코드 2의 차단이 유지되고 JSON의
"allow"도 통과시키지 못한다. Claude Code 훅 레퍼런스 → 훅 입출력 규칙 - 이벤트가 막을 수 있는지 확인한다.
PostToolUse는 도구가 이미 실행됐고,PermissionRequest는 종료 코드 2를 받지 않고decision.behavior: "deny"로만 거부한다.ConfigChange는policy_settings변경을 막지 못한다. → 훅 이벤트 레퍼런스Claude Code 훅 레퍼런스
2단계: 훅이 실패하면 막게 한다 (onFailure, v2.1.295 이상)#
대부분의 이벤트에서 훅이 시작하지 못하거나, 0·2가 아닌 코드로 끝나거나, 시간을 넘기면 동작은 그대로 진행된다. 원본: "A policy hook with a wrong path or a crashing script then lets everything through." command·http 훅에 "onFailure": "block"을 둔다. Claude Code 훅 레퍼런스
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "node",
"args": ["${CLAUDE_PROJECT_DIR}/.claude/hooks/check-command.js"],
"onFailure": "block"
}
]
}
]
}
}- 시험:
check-command.js를 일부러 없앤 채ls같은 Bash 명령을 시킨다. 호출이 막히고 오류에failed; blocking because onFailure is "block"이 보여야 한다. 시간 초과면timed out으로 나온다.onFailure가 없으면 같은 상황에서ls가 실행된다. Claude Code 훅 레퍼런스 - 실패로 치는 것: 시작 실패, 0·2 아닌 종료 코드(허용 JSON을 찍었어도), HTTP 연결 실패·2xx 아닌 상태, 시간 초과, 잘못된 출력. JSON 결정을 내려면 종료 코드 0으로 끝낸다. Claude Code 훅 레퍼런스
"block"이면 실패가 그 이벤트의 종료 코드 2 동작이 된다.PermissionRequest에서만 예외로 요청을 거부한다. 원본: "With"block"set, a failure does what exit code 2 does on that event, except onPermissionRequest, where it denies the request." 원본 예로PreToolUse실패는 도구 호출을,UserPromptSubmit실패는 프롬프트를 막는다. 따라서 종료 코드 2로 막지 못하는 이벤트(PostToolUse는 도구가 이미 실행됨,SessionStart는 stderr를 사용자에게만 보임)에서는onFailure: "block"으로도 막을 수 없다. → 훅 이벤트 레퍼런스Claude Code 훅 레퍼런스- 한계:
Stop·SubagentStop·TaskCompleted·TeammateIdle훅과async·asyncRewake훅에는 효과가 없다. 원본은onFailure를command·HTTP 훅의 필드 표에만 적고, 시간 초과 때 막는 방법도 "commandorhttphook"에 대해서만 말한다. 그래서mcp_tool훅이 시간 초과·실패할 때 막는 방법은 원본에 없다. Claude Code 훅 레퍼런스 - 시간 초과에 주의:
PreToolUse에서 시간 초과된 command·http·mcp_tool 훅은 실패하면 막도록 설정한 훅이 아니면 호출을 막지 않는다(막는 설정은command·http의onFailure: "block"). 원본: "don't count on a stalled hook to act as a gate."UserPromptSubmit도 시간 초과(기본 30초)면 출력을 버리고 프롬프트를 그대로 보낸다. 예외로UserPromptSubmit의 Agent SDK 콜백 훅은 시간 초과면 훅과 시간 초과를 밝힌 메시지로 프롬프트를 막고 세션은 계속된다. 원본 이유: 그 자리의 콜백은 "a policy gate that must not fail open"일 수 있어서다. Claude Code 훅 레퍼런스 - Windows에서 Git Bash가 없으면
shell을 정하지 않은 셸 형식PreToolUse·PermissionRequest훅의 실패가 오히려 호출을 막을 수 있다. Bash용 훅이면 Git for Windows를 설치하거나CLAUDE_CODE_GIT_BASH_PATH를, PowerShell용이면"shell": "powershell"과"onFailure": "block"을, 실패해도 진행돼야 하면"onFailure": "continue"를 둔다. Claude Code 훅 레퍼런스
3단계: 첫 실행을 눈으로 확인한다#
settings.json의 경로를 잘못 쓰면 훅은 한 번도 돌지 않는다. 원본은 정책 훅을 설정한 뒤 첫 실행에서Failed with non-blocking status code: /bin/sh: /path/to/hook.sh: No such file or directory같은 알림을 살피라고 한다. Claude Code 훅 레퍼런스- 디버그 로그(
claude --debug-file <path>, 또는claude --debug후~/.claude/debug/<session-id>.txt)에서Hooks: PostToolUse:Write [.claude/hooks/log-write.sh] finished with status 0 (31ms)(원본 예) 같은 줄로 실제 실행과 결과를 본다(v2.1.296 이상). → Claude Code 훅 문제 해결Claude Code 훅 레퍼런스
4단계: 우회 경로를 matcher로 덮는다#
원본이 직접 밝힌, 훅이 보지 못하는 경로다. 정책마다 해당하는 것을 확인한다. Claude Code 훅 레퍼런스
| 막으려는 것 | 훅이 놓치는 경로 | 원본이 권하는 대응 |
|---|---|---|
| 셸 명령 | Windows에서 PowerShell 도구로 간 명령 | matcher를 Bash|PowerShell로. if는 도구 하나만 맞추므로 핸들러를 도구별로 |
| 파일 편집 | Bash 명령이나 Claude Code 밖 프로세스가 고친 파일Edit|Write matcher가 안 봄) | 파일 자체는 FileChanged로 감시Bash|PowerShell도 맞추거나 턴마다 작업 트리를 훑는 Stop 훅을 권함 Claude Code 훅 가이드 |
| 파일 읽기 | 프롬프트의 @ 참조 | Read deny 규칙, 또는 CLAUDE_ |
| 스킬· | / 직접 입력Skill 도구 PreToolUse를 거치지 않음) | UserPromptExpansion 훅 |
| 워크트리 생성 | Claude가 Bash로 돌린 git worktree add | PreToolUse Bash 훅 |
| 경로 기준 차단 | Windows 역슬래시 경로 | 비교 전에 구분자 정규화FILE_), ^ 고정 대신 경로 조각/)으로 비교 |
| MCP 도구 | 플러그인에 묶인 서버mcp__), . 없는 서버 matcher | 범위 이름으로 matcher·if 작성, mcp__<server>__.처럼 . 붙이기 |
- 모델 전환은 반대 방향을 주의한다: Claude Code가 대상의 정식 이름을 알 수 없으면(예: LLM 게이트웨이만 아는 커스텀 모델 ID) matcher와 상관없이 모든
PreModelSwitch훅을 돌린다. 그래서 막는 훅은 matcher에만 기대지 말고 입력의to_model을 직접 확인하라고 원본이 말한다. 원본: "A hook that blocks should therefore checkto_modelfrom its input rather than rely on the matcher alone." Claude Code 훅 레퍼런스 - 좋은 소식도 있다:
Write·Edit·Read의file_path는 훅 전에 절대 경로로 펼쳐지므로~나 상대 경로 표기로는 경로 기준 훅을 우회할 수 없다고 원본이 말한다. Claude Code 훅 레퍼런스 - MCP 서버를 믿을지 판단할 때는 이름이나
mcp__<server>__접두어가 아니라 입력의mcp_server.source를 쓰라고 원본이 말한다(v2.1.274 이상). Claude Code 훅 레퍼런스 - 가이드가 밝힌 예외: 설치한 mod가
tool.check를 다루면, managed settings에 있지 않은PreToolUse훅이 막은 호출을 승인할 수 있다(plan 모드에서는 읽기 전용 호출만). Claude Code 훅 가이드 공식 권한 문서(2026-10-21 기준)는 이 예외를 세션 조건으로 적는다: 기기에 managed settings가 있거나 Team·Enterprise/login이 세션 자격 증명이면 훅이 막은 호출은 훅이 어느 파일에 있든 막힌 채 남고, 둘 다 없으면(API 키·CLAUDE_CODE_OAUTH_TOKEN세션 포함) mod가 훅 차단과 deny 거부를 승인할 수 있다. 이 점검은 권한 문서 기준으로 한다(가이드 표현과의 관계는 훅의 ‘원본끼리 달랐던 점’ 상자). 점검 항목: 정책을 거는 기기에 managed settings가 배포됐는지, 세션이 어떤 자격 증명으로 도는지. Claude Code 권한 설정
5단계: 결정을 먼저 찍고 백그라운드 일을 띄운다#
훅이 백그라운드 프로세스에서 결정을 내리고 먼저 끝나면, 나중에 찍힌 deny는 호출을 막지 못한다. 결정을 먼저 찍고, 백그라운드 프로세스의 stdout·stderr를 돌려 둔다. 허용을 찍은 뒤 출력을 돌리지 않은 백그라운드 프로세스를 띄우면 Claude Code가 오히려 호출을 막는다(refusing to honor the suspect capture). Claude Code 훅 레퍼런스
#!/bin/bash
"$CLAUDE_PROJECT_DIR"/.claude/hooks/policy-check.sh || exit $?
"$CLAUDE_PROJECT_DIR"/.claude/hooks/upload-metrics.sh >/dev/null 2>&1 &|| exit $?가 검사의 종료 코드(2)를 훅의 종료 코드로 만든다. 없으면 마지막 줄의 0으로 끝난다. 시험: policy-check.sh가 2로 끝나게 하고 맞는 도구 호출을 일으켜 막히는지 본다. Claude Code 훅 레퍼런스
6단계: 허용은 훅이 아니라 권한 규칙이 정한다는 것을 전제로 둔다#
- 훅의
"allow"(PreToolUse)나behavior: "allow"(PermissionRequest)를 돌려줘도 deny·ask 규칙은 평가된다.updatedInput으로 바꾼 입력도 권한 규칙으로 다시 평가된다. Claude Code 훅 레퍼런스 requiresUserInteraction으로 표시된 MCP 도구는 훅이 확인 창을 건너뛸 수 없다. Claude Code 훅 레퍼런스- 훅의
"ask"는 자동 모드에서도 확인 창을 강제한다(v2.1.211 이상). Claude Code 훅 레퍼런스 → 권한 모드 - 치명 경로를 겨냥한
rm·rmdir은 훅의"allow"로도 승인되지 않는다는 것은 권한 모드 문서가 든 항목이다. Claude Code 권한 모드 - (2026-10-21 기준) 공식 권한 문서가 더하는 것: 네트워크 경로 읽기와 조직이
ask로 정한 커넥터 도구(그 설정이 Claude Code 세션에 닿는 경우)도 훅의"allow"로 확인을 건너뛰지 못한다(네트워크 경로는 v2.1.292 이상). plan 모드에서는ExitPlanMode를 빼고 읽기 전용이 아닌 호출에"allow"가 효과가 없다. Claude Code 권한 설정 → 훅 이벤트 레퍼런스 - 반대 방향은 성립한다: 종료 코드 2로 막는 훅은 권한 규칙 평가 전에 호출을 멈추므로 allow 규칙보다 앞선다. 그래서 "대부분 허용하고 몇 개만 막기"는 allow에
"Bash"를 넣고 막을 명령만 거부하는PreToolUse훅으로 짤 수 있다고 원본은 말한다. 이때 Bash 규칙은 명령 텍스트만 맞추고/usr/bin/curl·sh -c '…'같은 다른 형태를 막지 않으므로, 막는 쪽은 훅의 검사 로직이 이 형태들을 다뤄야 한다(편집자 해석, 근거는 권한 문서의 "isn't a security boundary around the program" 표와 같은 절의 PreToolUse 훅 권고). Claude Code 권한 설정 → Claude Code 권한 규칙 문법
7단계: 훅이 지키지 못하는 것을 고객에게 분명히 한다#
- 막힌 프롬프트의 흔적:
UserPromptSubmit이 막아도 기본 차단 메시지에Original prompt:와 원문이 붙어 트랜스크립트 파일에 쓰인다.suppressOriginalPrompt: true는 메시지만 바꾸고 원문은 트랜스크립트·프롬프트 기록에 남을 수 있다. 원본: "a blocking hook isn't a way to keep a secret off disk." Claude Code 훅 레퍼런스 - 결과 가리기의 한계:
PostToolUse의updatedToolOutput은 Claude가 보는 것만 바꾼다. 파일 쓰기·명령·네트워크 요청은 이미 일어났고, OpenTelemetry 도구 span 같은 원격 측정은 원래 출력을 이미 기록했다. 실행 자체를 막거나 바꾸려면PreToolUse를 쓴다. Claude Code 훅 레퍼런스 - 화면 가리기의 한계:
MessageDisplay의displayContent는 화면 표시만 바꾸고 트랜스크립트와 Claude가 보는 내용은 원문이다. Claude Code 훅 레퍼런스 - 변경 목록의 한계:
PostToolUse의tool_response.bashEditDiff는 공개 베타의 최선 노력 목록이라 원본은 정책 강제가 아니라 검토 대상 찾기에 쓰라고 한다. 게다가bashEditDiffEnabled를 켜지 않으면 자동 모드·bypassPermissions모드에서 Claude Code가 Bash 편집을 지시할 때만 기록된다. Claude Code 훅 레퍼런스 - 분류기 메모:
classifierContext에는 신뢰할 수 없는 도구 출력이나 제3자 텍스트를 복사하지 말라고 원본이 경고한다. 분류기는 그것을 세션을 호스팅하는 애플리케이션이 준 정보로 읽는다. settings·플러그인·스킬·에이전트 frontmatter의 훅이 준 메모는 검증되지 않은 맥락으로 다뤄지고 사용자 의도를 세우지 못한다. Claude Code 훅 레퍼런스
8단계: 남의 저장소와 자동 실행을 조심한다#
- command 훅은 사용자의 전체 권한으로 셸 명령을 실행한다. 원본: 계정이 접근할 수 있는 모든 파일을 고치거나 지우거나 읽을 수 있으니 설정에 넣기 전에 검토·시험하라. Claude Code 훅 레퍼런스
- 작업 공간 신뢰: 대화형 세션은 그 폴더, 또는 신뢰가 그 폴더까지 이어지는 상위 디렉터리의 신뢰 대화상자를 수락하기 전까지 모든 settings 파일(자기
~/.claude/settings.json포함)의 훅을 보류한다.-p·SDK 세션은 대화상자 없이 폴더를 신뢰한 것으로 다루므로 저장소에 커밋된.claude/settings.json의 훅이 처음 보는 폴더에서도 돈다. 스킬 frontmatter 훅도 settings 파일과 같은 규칙이라 신뢰하지 않은 폴더의-p실행에서도 스킬을 부르면 등록된다. 프로젝트 서브에이전트 frontmatter 훅은 더 엄격해서 신뢰 대화상자를 수락한 뒤에만 돌고-p는 수락으로 치지 않는다(v2.1.218 이상). Claude Code 훅 레퍼런스 무엇이 신뢰 전에 도는지 한 표로 본 것(2026-10-21 기준, 공식 권한 문서): 작업 공간 신뢰 - 남이 만든 저장소에
claude -p를 스크립트로 돌리기 전 원본이 권하는 셋 중 하나:.claude/설정 파일 검토,--bare로 시작,--settings '{"disableAllHooks": true}'로 그 실행의 훅 끄기. 단disableAllHooks는 managed settings 밖에서 managed 훅을 끄지 못한다. Claude Code 훅 레퍼런스 공식 CLI 문서(2026-10-21 기준)는--bare가 건너뛰는 것을 훅, 스킬, 커스텀 명령, 서브에이전트, 설치된 플러그인, MCP 서버, 자동 메모리, CLAUDE.md의 자동 탐색으로 적는다. 예외로--add-dir로 준 디렉터리의 스킬은 로드되고, Bash·파일 읽기·편집 도구는 그대로 있다. 진단용--safe-mode는 사용자 훅을 끄지만 managed 정책으로 설정한 훅은 계속 돈다. 공유 기기에서 평가 하네스가 돌릴 때의--restricted(v2.1.248 이상)는 명령·코드 실행 도구와 WebFetch를 빼고(--tools에 개별 이름으로 지정하면 다시 들어옴), managed settings와--settings만 로드한다. Claude Code CLI 레퍼런스 → Claude Code CLI 플래그 - (2026-10-21 기준) 공식 권한 문서의 목록은 넷이다:
--setting-sources user(또는 SDKsettingSources에서 project 빼기)로 프로젝트 설정 파일과.mcp.json을 아예 읽지 않기,--bare(단 프로젝트env블록과awsAuthRefresh같은 헬퍼는 여전히 적용,apiKeyHelper는--settings에서만 읽음),--settings '{"disableAllHooks": true}'(사용자 설정에만 두면 프로젝트 설정이false로 되돌릴 수 있어 부족),disabledMcpjsonServers로.mcp.json서버를 이름으로 거부. Claude Code 권한 설정 → 작업 공간 신뢰 - 원본의 보안 습관 다섯 가지: 입력을 검증·정리하고 맹신하지 않는다, 셸 변수는 항상
"$VAR"로 따옴표, 경로의..로 디렉터리 이탈 막기, 스크립트는 절대 경로로(exec form은${CLAUDE_PROJECT_DIR}를 따옴표 없이, shell form은 큰따옴표로),.env·.git/·키 같은 민감 파일 건너뛰기. Claude Code 훅 레퍼런스
9단계: 조직 단위로 잠근다#
- 사용자가 훅을 끄지 못하게: managed settings에 둔 훅은 user·project·local의
disableAllHooks로 꺼지지 않고, managed 수준의disableAllHooks만 끈다. Claude Code 훅 레퍼런스 - 다른 출처의 훅을 막으려면: managed
allowManagedHooksOnly. 켜면 user·project·local·플러그인 훅이 막힌다(managedenabledPlugins로 강제 활성화한 플러그인의 훅은 예외). 함께statusLine·fileSuggestion·subagentStatusLine이 managed 설정으로 좁혀지고,disableCommandPluginSources를 명시적으로false로 두지 않았다면command출처 플러그인(managed로 강제 활성화한 것 포함)과 마켓플레이스headersHelper명령(managed 설정이 직접 선언한 마켓플레이스는 예외)도 막히는 부수 효과가 있다. → managed settingsClaude Code 훅 레퍼런스 - HTTP 훅 허용 목록(
allowedHttpHookUrls,httpHookAllowedEnvVars)은 managed 정책을 포함한 모든 출처의 훅에 적용된다. Claude Code 훅 레퍼런스 - 설정 변경 감사:
ConfigChange훅으로 세션 중 설정·스킬 파일 변경을 기록하거나 막는다.policy_settings는 막을 수 없어 로그만 되고, 서버 관리 설정·macOS managed preferences·Windows 레지스트리 정책 변경에는 이 훅이 돌지 않는다. Claude Code 훅 레퍼런스 → Claude Code 관리자 권한 통제
고객 설명용 한 줄 (편집자 정리)#
편집자 해석: 원본의 "staying silent doesn't approve it", "lets everything through", "best-effort" 세 문장을 합치면, 차단 훅은 "막는 신호(종료 코드 2)를 정확히 내고, 실패하면 막도록(onFailure: "block") 설정하고, 확실한 허용·거부는 권한 규칙에 맡길 때" 믿을 만하다고 정리할 수 있다. 설명할 때는 "훅은 문지기, 권한 규칙은 자물쇠"로 비유할 수 있으나 이 비유는 원본에 없다.