
Claude Code의 deny는 git 명령어를 우회한다 — hooks로 방어한 이야기
요약
Claude Code의 deny 설정이 git 명령어를 통해 우회될 수 있는 보안 취약점을 분석하고, 이를 hooks 메커니즘으로 방어하는 방법을 다룹니다. PreToolUse 쉘 스크립트를 활용해 특정 git 명령어가 보호된 파일에 접근하는 것을 차단하는 가드 로직을 구현합니다.
핵심 포인트
- Claude Code의 deny 설정은 Bash 툴을 통한 git 명령어 접근을 막지 못함
- git diff, git show 등을 통해 deny된 파일의 내용을 파악할 수 있는 우회 경로 존재
- PreToolUse hooks를 사용하여 Bash 명령어 실행 전 보안 검사 가능
- 명령어 패턴 매칭을 통해 특정 git 명령어와 보호 경로를 결합하여 차단 구현
서론
deny(거부)했을 파일인데, Claude Code가 읽고 있었다.
Claude Code에는 특정 파일을 AI에게 읽히지 않기 위한 deny 설정이 있다. 기밀성이 높은 비즈니스 로직을 포함하는 파일이나, 외부에 노출하고 싶지 않은 코드에 사용했다. 설정을 마친 직후에는 "이걸로 안심이다"라고 생각했다.
하지만 어느 날, Claude Code가 그 파일의 내용을 파악하고 있는 것 같다는 사실을 깨달았다. deny했을 파일의 함수명이나 변수명을 정확하게 대답해 온 것이다.
원인은 git diff였다.
deny를 우회하는 경로
Claude Code의 deny는 Read 툴을 통한 직접적인 파일 읽기를 차단한다. 반면, Bash 툴은 별개로 취급된다.
즉, 다음과 같은 명령어들은 그대로 통과한다.
git diff HEAD~1 -- src/core/ScoreCalculator.ts
git show HEAD:src/core/PriorityResolver.ts
git blame src/core/ScoreCalculator.ts
...
이것들은 모두 대상 파일의 내용을 출력한다. git diff는 deny된 파일의 변경 차분(diff)을 통째로 보여주고, git show는 임의의 커밋 시점의 내용을 복원한다. Read를 차단하더라도, git 명령어를 경유하여 동일한 정보에 접근할 수 있는 것이다.
테스트 삼아 Claude Code에게 "최근 이 파일에 어떤 변경 사항이 있었는지 확인해줘"라고 부탁해 보았더니, git log -p를 사용하여 내용을 읽고 있었다.
hooks로 막기
Claude Code에는 hooks라는 메커니즘이 있다. 툴의 실행 전후에 쉘 스크립트(shell script)를 개입시킬 수 있다. PreToolUse를 사용하면 Bash 명령어 실행 전에 내용을 검사하여, 조건에 따라 차단할 수 있다.
이를 사용하여 git의 콘텐츠 취득 명령어에 대한 가드(guard)를 만들었다.
{
"permissions": {
"deny": [
...
#!/bin/bash
INPUT=$(cat)
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // """)
...
메커니즘은 심플하다.
- Bash 툴이 실행될 때마다 스크립트가 기동한다.
- 명령어가
git show,git blame,git diff,git log -p중 하나인지 확인한다. - 보호 대상 경로(path)가 포함되어 있다면 차단한다.
차단할 때는 표준 출력(standard output)으로 JSON을 반환한다. "decision": "block"을 포함하면, Claude Code는 해당 명령어를 실행하지 않는다.
좋았던 점
deny와 hooks를 조합함으로써, "Read로는 읽을 수 없지만, git 명령어로는 읽을 수 있다"라는 허점을 크게 줄일 수 있었다.
Claude Code에게 "이 파일을 차분을 포함해서 확인해줘"라고 요청해도, 차단되어 보호된 파일에 대한 git 콘텐츠 접근은 금지되어 있습니다라고 돌아온다. 명시적인 에러 메시지가 나오기 때문에 무엇이 일어나고 있는지 알기 쉽다.
그 이상으로 수확이었던 것은, hooks라는 메커니즘의 존재를 실감하며 이해할 수 있었다는 점이다. deny와 같은 선언적인 설정으로는 대처할 수 없는 "우회로"에 대해, 스스로 로직을 끼워 넣을 수 있다. Claude Code와 함께하는 방식의 폭이 넓어진 느낌이 든다.
아직 남아있는 과제
현재 스크립트에는 허점이 있다. git diff --staged와 같이 파일 경로를 지정하지 않는 형식의 명령어다.
# 이것은 차단되지 않는다
git diff --staged
git diff HEAD
...
명령어 문자열 안에 보호 경로가 나타나지 않기 때문에, 패턴 매칭(pattern matching)으로 검출할 수 없다. 하지만 실행하면 스테이징(staging)이나 이력에 포함된 보호 파일의 차분이 그대로 출력된다.
이 문제는 아직 해결하지 못했다. 좋은 방법이 있다면 댓글로 알려주면 감사하겠다.
요약
Claude Code의 deny는 Read 툴만을 차단한다. git 명령어를 통한 접근은 별도의 대처가 필요하다.
PreToolUse hook를 사용하면 Bash 명령어 실행 전에 검사를 끼워 넣을 수 있다. 완전히 다 막아낸 것은 아니지만, 아무런 대책을 세우지 않는 것보다는 확실하게 리스크를 낮출 수 있다.
코드베이스에 보여주고 싶지 않은 파일이 있고, 동시에 Claude Code를 적극적으로 사용하고 있는 분들에게는 deny뿐만 아니라 hooks도 함께 조합하여 사용할 것을 권장한다.
Discussion

AI 자동 생성 콘텐츠
본 콘텐츠는 Zenn AI의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기