Tips & Tricks (업데이트: 2026. 6. 3.)

Claude Code 보안 실패 사례 7가지: 원인, 복구, 재발 방지

.env 유출, 운영DB 삭제, CI 비용 폭주를 Claude Code 보안 사례로 예방하는 실전 가이드.

Claude Code 보안 실패 사례 7가지: 원인, 복구, 재발 방지

Claude Code는 단순히 코드를 제안하는 채팅창이 아닙니다. 파일을 수정하고, 테스트를 실행하고, Git 상태를 읽고, 배포 명령까지 제안할 수 있는 개발 에이전트입니다. 편리하지만, 지친 상태에서 권한 확인을 계속 승인하면 사람이 직접 입력했을 때와 같은 사고가 더 빠르게 발생합니다.

이 글은 Claude Code를 사용할 때 자주 생기는 보안 사고를 7가지로 나누고, 바로 복사해서 쓸 수 있는 방어 설정을 제시합니다. 처음 쓰는 사람을 위해 설명하면 permission은 “에이전트가 해도 되는 일의 경계”, hook은 “도구 실행 전후의 검문소”, sandbox는 “실수해도 영향 범위를 줄이는 격리 작업실”입니다.

flowchart TD
  Request["User request"] --> Plan["Claude Code plan"]
  Plan --> Permission["Permission rules"]
  Permission --> Hook["PreToolUse hook"]
  Hook --> Execute["Tool execution"]
  Execute --> Audit["Log, review, recovery"]
  Hook -->|block risky command| Stop["Stop before damage"]

먼저 보는 실패 지도

실패 사례자주 일어나는 순간피해먼저 둘 안전장치
.env 커밋”CI에서도 쓰니 같이 올려줘”API 키 유출, 과금 피해.gitignore와 staged 파일 검사
운영DB 삭제migration이 잘못된 URL에 연결데이터 손실, 복구 작업DB 명령 전 환경명 확인
git push --force충돌 해결을 급하게 처리팀원의 커밋 손실ask 규칙과 보호 브랜치
CI의 AI 리뷰 폭주모든 PR에서 넓은 자동화 실행Actions 시간과 API 비용 증가max-turns, timeout, 최소 권한
신뢰할 수 없는 로그 붙여넣기로그 안에 숨은 지시문 포함prompt injection위험한 Web/Bash 가져오기 거부
MCP 권한 과다편리하다는 이유로 외부 서버 신뢰로컬 데이터 과다 읽기신뢰한 MCP 서버만 허용
승인 피로사용자가 계속 확인을 누름위험 명령 통과hook으로 자동 차단

핵심은 Claude Code가 기본적으로 위험하다는 뜻이 아닙니다. 공식 Security 문서는 읽기 중심 기본 권한, 권한 확인, 샌드박스, 사용자 책임을 설명합니다. 실제 위험은 바쁜 팀에서 사람이 확인을 생략하는 데 있습니다. 그래서 안전한 동작은 쉽게, 위험한 동작은 멈추게 만드는 설정이 필요합니다.

유스케이스1: 개인 프로젝트의 API 키 유출 막기

초보자가 가장 많이 착각하는 파일이 .env입니다. .env는 금고가 아니라 로컬 텍스트 파일입니다. Git, 로그, 스크린샷, 프롬프트에 들어가지 않을 때만 안전합니다. Stripe, SendGrid, Anthropic, GitHub token 같은 값이 공개 저장소에 들어가면 이미 유출된 것으로 봐야 합니다.

저장소에는 실제 값이 아니라 설정의 형태만 커밋합니다.

# .gitignore
.env
.env.*
!.env.example
secrets/
*.pem
*.key
*service-account*.json
credentials.json
# .env.example
ANTHROPIC_API_KEY=replace_me
DATABASE_URL=postgres://app_user:password@localhost:5432/app_dev
STRIPE_SECRET_KEY=sk_test_replace_me

그다음 Claude Code 권한을 조입니다. deny는 거부, ask는 매번 확인, allow는 반복되는 안전 명령을 자동 허용한다는 뜻입니다. 공식 SettingsConfigure permissions 문서는 이 규칙이 어떻게 평가되는지 설명합니다.

{
  "$schema": "https://json.schemastore.org/claude-code-settings.json",
  "permissions": {
    "allow": [
      "Bash(npm run lint)",
      "Bash(npm run test *)",
      "Bash(git status)",
      "Bash(git diff *)"
    ],
    "ask": [
      "Bash(git push *)",
      "Bash(npm run deploy *)",
      "Write(./migrations/**)"
    ],
    "deny": [
      "Read(./.env)",
      "Read(./.env.*)",
      "Read(./secrets/**)",
      "Bash(rm -rf *)",
      "Bash(curl *)",
      "Bash(wget *)",
      "WebFetch"
    ]
  }
}

이 설정은 Claude Code를 못 쓰게 만드는 것이 아닙니다. 안전한 반복 작업은 빠르게 만들고, 파괴적인 작업은 반드시 눈에 띄게 만드는 설정입니다. 웹 접근이 필요한 프로젝트라면 처음부터 전부 허용하지 말고, 허용할 도메인과 목적을 정한 뒤 좁게 풀어야 합니다.

유스케이스2: 팀 PR 리뷰가 형식적으로 흐르지 않게 하기

팀에서 자주 생기는 문제는 “AI가 리뷰했으니 됐다”는 분위기입니다. 그러면 사람은 권한 파일, workflow 변경, credential 처리를 덜 보게 됩니다. 바로 그 지점에서 보안 문제가 통과합니다.

아래 스크립트는 외부 의존성이 없습니다. 기본값은 staged 파일 검사이고, CI에서는 --all로 전체 저장소를 검사할 수 있습니다.

// scripts/claude-security-check.mjs
import { execFileSync } from "node:child_process";
import fs from "node:fs";

const args = process.argv.slice(2);
const scanAll = args.includes("--all");
const explicitFiles = args.filter((arg) => arg !== "--all");

function runGit(args) {
  return execFileSync("git", args, { encoding: "utf8", maxBuffer: 10 * 1024 * 1024 });
}

function filesToScan() {
  if (explicitFiles.length > 0) return explicitFiles;
  if (scanAll) return runGit(["ls-files"]).split(/\r?\n/).filter(Boolean);
  return runGit(["diff", "--cached", "--name-only"]).split(/\r?\n/).filter(Boolean);
}

function readTrackedOrWorkingTree(file) {
  if (scanAll || explicitFiles.length > 0) return fs.readFileSync(file, "utf8");
  return runGit(["show", `:${file}`]);
}

const forbiddenPath = [
  /^\.env$/,
  /^\.env\./,
  /(^|\/)secrets\//,
  /(^|\/).*service-account.*\.json$/i,
  /(^|\/)credentials\.json$/i,
  /\.(pem|key)$/i
];

const secretPattern =
  /(sk-ant-[A-Za-z0-9_-]{20,}|sk_live_[A-Za-z0-9_-]{20,}|AKIA[0-9A-Z]{16}|-----BEGIN (?:RSA |EC |OPENSSH )?PRIVATE KEY-----)/;

let failed = false;

for (const file of filesToScan()) {
  if (forbiddenPath.some((pattern) => pattern.test(file))) {
    console.error(`[blocked] forbidden secret path: ${file}`);
    failed = true;
    continue;
  }

  try {
    const text = readTrackedOrWorkingTree(file);
    if (secretPattern.test(text)) {
      console.error(`[blocked] secret-like value found in: ${file}`);
      failed = true;
    }
  } catch {
    // Ignore deleted or binary files.
  }
}

if (failed) process.exit(1);
console.log("security check passed");
{
  "scripts": {
    "security:staged": "node scripts/claude-security-check.mjs",
    "security:all": "node scripts/claude-security-check.mjs --all"
  }
}

주의할 점은 스크립트를 만들고 끝내는 것입니다. npm run security:staged를 커밋 전 체크리스트, PR 템플릿, CI 중 하나에 반드시 넣어야 합니다. Claude Code에게 “보안 검사도 실행해 줘”라고 말하는 것보다 실패하는 명령을 배치하는 편이 훨씬 안정적입니다.

유스케이스3: 운영DB와 CI 비용 보호하기

운영 사고는 대개 환경 혼동에서 시작됩니다. DROP TABLE 자체보다 더 큰 문제는 개발DB라고 생각하고 운영 DATABASE_URL에 연결하는 것입니다. Claude Code에게 오래된 데이터를 지우라고 하기 전에 DB 라벨, 클라우드 프로젝트, 브랜치명, 백업 시각을 사람이 읽을 수 있게 출력하세요.

GitHub Actions에서 Claude Code를 쓸 때는 공식 GitHub Actions 문서가 설명하듯 API 키는 Secrets에 두고 필요한 권한만 줍니다. 아래 workflow는 PR diff 리뷰만 수행하고 저장소 쓰기 권한을 주지 않습니다.

name: Claude Code guarded review

"on":
  pull_request:
    types: [opened, synchronize, reopened]

permissions:
  contents: read
  pull-requests: write

jobs:
  claude-security-review:
    runs-on: ubuntu-latest
    timeout-minutes: 15
    steps:
      - uses: actions/checkout@v4
        with:
          persist-credentials: false

      - name: Run repository secret scan
        run: node scripts/claude-security-check.mjs --all

      - uses: anthropics/claude-code-action@v1
        with:
          anthropic_api_key: "${{ secrets.ANTHROPIC_API_KEY }}"
          prompt: >
            Review only the pull request diff for secret handling, auth checks,
            destructive commands, and permission changes. Do not modify files.
          claude_args: |
            --max-turns 3
            --disallowedTools "Bash(git push *)" "Bash(npm run deploy *)" "Bash(rm -rf *)"

permissions: contents: read가 중요한 이유는 리뷰 전용 job에는 쓰기 권한이 필요 없기 때문입니다. timeout-minutes--max-turns도 보안 장치입니다. 자동화가 끝나지 않으면 비용과 가용성 사고가 됩니다.

이미 사고가 났을 때의 복구 순서

API 키가 유출되었다면 Git 히스토리 정리부터 시작하지 마세요. 먼저 제공자 콘솔에서 키를 폐기하거나 교체해야 합니다. 저장소가 깨끗해져도 이미 복사된 값은 계속 사용될 수 있습니다.

  1. 노출된 키를 폐기하거나 교체합니다.
  2. 노출 기간의 사용 로그와 청구 내역을 확인합니다.
  3. GitHub Secrets, 배포 플랫폼 변수, CI 값을 바꿉니다.
  4. Git 히스토리에서 값을 제거하고 필요하면 캐시 삭제를 요청합니다.
  5. 같은 수정 PR에 .gitignore, 권한 설정, 검사 스크립트를 추가합니다.

운영 데이터가 손상되었다면 복구 명령을 바로 실행하지 말고 먼저 쓰기를 멈추거나 읽기 전용으로 전환합니다. 백업 시각, 복구 대상, 허용 가능한 데이터 손실 범위를 정한 뒤 한 명령씩 로그를 남기며 진행합니다.

공식 문서, 내부 링크, 다음 단계

공식 Security, Configure permissions, Settings, Hooks, GitHub Actions를 기준 문서로 보세요. 특히 hooks는 도구 실행 전에 자체 검사를 넣을 수 있어 팀 운영에서 매우 유용합니다.

사이트 안에서는 /ko/blog/claude-code-security-best-practices/, /ko/blog/claude-code-permissions-guide/, /ko/blog/claude-code-secrets-management/를 함께 읽으면 사고 대응, 권한 설계, 시크릿 관리가 하나로 이어집니다.

바로 쓸 템플릿이 필요하다면 /products/의 설정 가이드와 체크리스트를 활용하세요. 팀 도입 절차, CI 정책, 리뷰 흐름까지 정리해야 한다면 /training/의 상담과 교육 옵션이 첫 권한 모델을 만드는 데 맞습니다.

Masa의 로컬 테스트 저장소에서는 이 설정이 .env 오스테이징, 가짜 sk_live_ 문자열, 과도한 GitHub Actions 리뷰 권한을 게시 전에 막았습니다. 다만 정규식 검사는 모든 키 형식을 잡지 못합니다. 장기적으로는 제공자 측 로테이션, 최소 권한, 자동 검사, Claude Code 작업 승인 전 사람의 리뷰를 함께 써야 합니다.

#claude-code #security #incident #best-practices #devops
무료

무료 PDF: Claude Code 치트시트

이메일을 입력하면 명령, 리뷰 습관, 안전한 워크플로를 정리한 PDF를 받을 수 있습니다.

개인정보를 안전하게 관리하며 스팸을 보내지 않습니다.

Masa

작성자 소개

Masa

Claude Code 실무 워크플로와 팀 도입을 검증하는 엔지니어입니다.