본문 바로가기
2026년 5월 26일2분 읽기

Claude Code 2.0 hooks가 production용 secret 차단인데 PR마다 새로 작성 중

김
김영삼
조회 373댓글 4

Claude Code 2.0의 hooks API로 secret 차단 hook을 만들어 쓰는데 매 PR마다 패턴 누락이 나옵니다. 사내 보안팀이 더 엄격해지면서 false negative를 줄여야 합니다.

현재 hook:

// hooks/check-secret.js
const content = process.env.CLAUDE_TOOL_INPUT
const patterns = [
  /AKIA[0-9A-Z]{16}/,            // AWS access key
  /sk-[a-zA-Z0-9]{20,}/,         // OpenAI key
  /ghp_[a-zA-Z0-9]{36}/,         // GitHub PAT
  /xoxb-[a-zA-Z0-9-]+/,          // Slack bot token
]
if (patterns.some(p => p.test(content))) {
  console.error('Secret detected')
  process.exit(2)  // Claude에게 차단 메시지
}

문제:

  1. 새 서비스의 새 토큰 prefix가 자꾸 늘어남 (Anthropic ant-, Vercel team-, etc.)
  2. private key(SSH·TLS)는 multi-line이라 regex 안 잡힘
  3. true secret이 base64 인코딩되어 들어오면 못 잡음
  4. 코드 안의 placeholder("YOUR_API_KEY")도 false positive 발생

실제로 검증된 패턴 셋업 공유 가능하실까요?

댓글 4

김김영삼5개월 전

저희 팀에서 검증된 패턴 공유합니다. 핵심은 "regex만으로 안 한다"입니다.


1. detect-secrets 라이브러리 활용


Yelp의 detect-secrets는 100+ 패턴 + entropy 분석. python인데 hook에서 호출 가능:


#!/usr/bin/env node
const { execSync } = require('child_process')
const content = process.env.CLAUDE_TOOL_INPUT

try {
const out = execSync(`echo ${JSON.stringify(content)} | detect-secrets scan --string`, { encoding: 'utf8' })
const result = JSON.parse(out)
if (Object.keys(result.results).length > 0) {
console.error('detect-secrets found:', JSON.stringify(result.results))
process.exit(2)
}
} catch (e) { /* clean */ }

regex + entropy + 라이브러리 룰 합쳐서 false negative 크게 줄어듭니다.


2. 패턴 셋 — 2026년 5월 기준 검증된 prefix


const PATTERNS = {
aws: /AKIA[0-9A-Z]{16}/,
aws_secret: /[A-Za-z0-9+/]{40}/, // entropy check 동반
openai: /sk-(?:proj-)?[a-zA-Z0-9_-]{20,}/,
anthropic: /sk-ant-(?:api\d{2}-)?[a-zA-Z0-9_-]{20,}/,
google_api: /AIza[0-9A-Za-z_-]{35}/,
github: /gh[ps]_[A-Za-z0-9_]{36,255}/,
gitlab: /glpat-[A-Za-z0-9_-]{20,}/,
slack: /xox[bp]-[0-9]{10,13}-[0-9]{10,13}-[A-Za-z0-9]{24,}/,
stripe: /sk_(?:test|live)_[A-Za-z0-9]{20,}/,
twilio: /AC[a-f0-9]{32}/,
notion: /secret_[A-Za-z0-9]{43}/,
vercel: /[A-Za-z0-9]{24,}_team_/,
jwt: /eyJ[A-Za-z0-9_-]{20,}\.[A-Za-z0-9_-]{20,}\.[A-Za-z0-9_-]{20,}/,
}

const PEM = /-----BEGIN (?:RSA |EC |DSA |OPENSSH )?PRIVATE KEY-----/

3. placeholder false-positive 제외


const PLACEHOLDERS = [
/YOUR_[A-Z_]+_KEY/,
//i,
/XXXX+/,
/\$\{[A-Z_]+\}/, // env 변수 참조
/process\.env\./,
]
function isPlaceholder(match) {
return PLACEHOLDERS.some(p => p.test(match))
}

4. base64 인코딩 secret 감지


base64 디코드 후 다시 패턴 매칭. 운영 부담 크면 skip해도 됩니다. 의도적으로 base64 인코딩한 secret 유출은 드뭅니다.


5. multiple-file commit 처리


Edit 도구는 single file이지만 MultiEdit는 여러 파일. hook input에 file path 별로 분기 처리 필요.


6. .env.example는 통과


.env.example, .env.template, .env.sample은 의도적 placeholder. file path 체크해서 통과.


const filePath = JSON.parse(process.env.CLAUDE_TOOL_INPUT).file_path
if (filePath?.includes('.env.example') || filePath?.includes('.env.template')) {
process.exit(0) // skip
}

이 6가지 적용하면 저희는 3개월 동안 단 1건 false negative 있었습니다. 그 1건은 새 서비스 prefix 누락이라 즉시 추가.

보보안전문가태호5개월 전

보안 관점에서 한 가지 추가 — hook이 자체 차단되어도 audit log는 남기세요. "차단된 secret detected"라는 이벤트 자체가 인시던트 단서입니다. SQLite 또는 Slack webhook으로 로깅 권장.


// 추가 — 차단 시 로그
if (detected) {
await logToSlack({ pattern, file, user: os.userInfo().username, ts: Date.now() })
process.exit(2)
}
ssec_pro(guest)5개월 전
git pre-commit에 같은 hook 두는 것도 권장. Claude Code hook이 차단해도 사람이 수동으로 만든 변경은 안 잡힙니다. 두 layer로 방어.
조조은비(guest)5개월 전
secret detection 무거운 라이브러리(gitleaks, trufflehog)는 PreToolUse에서 매번 돌리면 응답 지연이 1초 이상 됩니다. 가벼운 regex는 hook에서, 무거운 검사는 git pre-push 또는 CI에서 분리 권장.
Ctrl+Enter로 등록