AI에게 시크릿 안 넘기는 방법
1Password와 Vault CLI를 활용해서 민감정보를 런타임에만 주입하는 실전 가이드
시크릿, AI한테 그냥 넘기고 있지 않은가
Claude Code나 Cursor 같은 AI 코딩 도구를 쓰다 보면, API 키나 비밀번호를 넘겨야 할 때가 있다. 배포 스크립트를 짜달라고 하거나, 외부 API 연동 코드를 작성할 때.
그때 습관적으로 이렇게 하게 된다.
# 이러면 안 된다
export API_KEY="sk-1234567890abcdef"혹은 .env 파일에 넣어두고 AI가 읽게 하거나.
문제는, 이 시크릿이 AI 서버로 전송될 수 있다는 거다. 프롬프트에 포함되면 그대로 넘어간다. 파일을 읽으라고 시켰는데 .env를 통째로 읽어버리면? 그 내용이 컨텍스트에 올라간다.
개인 프로젝트라면 "뭐 어때"라고 넘길 수 있지만, 조직에서 쓰는 시크릿이라면 얘기가 달라진다.
.env, 이제 그만 쓸 때가 됐다
.env 파일의 문제는 단순히 AI에게 노출되는 것만이 아니다.
최근 .env 파일을 탈취하는 악성코드가 급증하고 있다. npm 패키지, VS Code 확장, 심지어 GitHub Action까지 — 악성 코드가 빌드 타임이나 설치 시점에 .env 파일을 읽어서 외부로 전송하는 사례가 늘고 있다.
.env는 결국 평문 텍스트 파일이다. 디스크에 그대로 놓여 있고, 읽기 권한만 있으면 누구든 볼 수 있다. 파일 시스템에 접근할 수 있는 모든 프로세스가 잠재적 위협이 된다.
핵심은 이거다: 시크릿은 안전한 곳에 보관하고, 내가 승인할 때만 접근 가능해야 한다. 지문이든 비밀번호든, 사람이 명시적으로 허용한 순간에만 꺼낼 수 있어야 한다.
조직 외부: 1Password CLI
개인 프로젝트나 조직 외부에서는 1Password CLI(op)를 쓰고 있다.
1Password는 이미 비밀번호 관리자로 많이 쓰고 있을 텐데, CLI가 있다는 걸 모르는 사람이 많다.
설치
# macOS
brew install --cask 1password-cli
# Linux
# https://developer.1password.com/docs/cli/get-started/시크릿 저장
1Password 앱에서 시크릿을 저장해둔다. 예를 들어 "OpenAI API Key"라는 이름으로 API 키를 저장했다고 치자.
런타임 주입
# 시크릿을 환경변수로 주입 — 실행 시점에 1Password에서 꺼내온다
export OPENAI_API_KEY=$(op read "op://Personal/OpenAI API Key/credential")이렇게 하면 실행하는 순간에 1Password가 인증을 요청한다. Touch ID나 마스터 비밀번호를 입력해야 값이 나온다.
더 깔끔한 방법도 있다. op run을 쓰면 .env 파일의 참조만 정의해두고, 실행 시점에 실제 값으로 치환한다.
# .env.tpl (이 파일에는 실제 시크릿이 없다)
OPENAI_API_KEY=op://Personal/OpenAI API Key/credential
DATABASE_URL=op://Development/PostgreSQL/connection-string
# 실행
op run --env-file=.env.tpl -- npm run dev.env.tpl 파일은 Git에 커밋해도 안전하다. 실제 값이 아니라 1Password 경로만 들어있으니까.
AI가 이 파일을 읽어도 op://Personal/OpenAI API Key/credential이라는 경로만 보일 뿐, 실제 키는 볼 수 없다.
AI 도구와 함께 쓰기
# Claude Code를 1Password 컨텍스트에서 실행
op run --env-file=.env.tpl -- claude
# 또는 특정 시크릿만 주입
ANTHROPIC_API_KEY=$(op read "op://Dev/Anthropic/api-key") claude시크릿이 프로세스의 환경변수로만 존재하고, 파일 시스템에는 평문으로 남지 않는다. AI가 env 명령을 실행하면 보일 수 있지만, 적어도 .env 파일을 통째로 읽어서 전송하는 것보다는 훨씬 안전하다.
조직 내부: HashiCorp Vault
조직 내부에서는 1Password 대신 HashiCorp Vault를 활용한다.
Vault는 시크릿 관리 전용 도구다. 접근 제어, 감사 로그, 자동 로테이션까지 지원한다. 그리고 핵심은 — Vault도 CLI가 있다.
Vault CLI 기본 사용법
# 시크릿 저장
vault kv put secret/myapp/api-keys openai="sk-xxx" anthropic="sk-yyy"
# 시크릿 조회
vault kv get -field=openai secret/myapp/api-keys런타임 주입 패턴
1Password의 op read와 같은 패턴이다.
# 환경변수로 주입
export OPENAI_API_KEY=$(vault kv get -field=openai secret/myapp/api-keys)
# 또는 스크립트에서
#!/bin/bash
OPENAI_API_KEY=$(vault kv get -field=openai secret/myapp/api-keys) \
ANTHROPIC_API_KEY=$(vault kv get -field=anthropic secret/myapp/api-keys) \
claude접근 제어: AppRole 토큰
Vault의 장점은 세분화된 접근 제어다.
# 특정 경로만 읽을 수 있는 정책 정의
vault policy write ai-readonly - <<EOF
path "secret/data/myapp/api-keys" {
capabilities = ["read"]
}
EOF
# AppRole 생성
vault auth enable approle
vault write auth/approle/role/ai-agent \
token_policies="ai-readonly" \
token_ttl=1h \
token_max_ttl=4hAI 에이전트용 토큰은 읽기 전용 + 짧은 TTL로 제한한다. 만약 토큰이 유출되더라도 1시간 후면 만료된다. .env 파일이 유출되면? 키를 수동으로 로테이션하기 전까지 영원히 유효하다.
AppRole 토큰을 Keychain에 보관
여기서 한 단계 더 나간다. Vault 인증에 쓰는 AppRole 토큰 자체도 시스템 Keychain에 보관할 수 있다.
# macOS Keychain에 Vault 토큰 저장
security add-generic-password \
-a "vault-ai-agent" \
-s "vault-token" \
-w "hvs.CAESIG..." \
-T "" \
/Library/Keychains/System.keychain
# Keychain에서 꺼내서 Vault 인증
export VAULT_TOKEN=$(security find-generic-password \
-a "vault-ai-agent" \
-s "vault-token" \
-w)Keychain도 CLI가 있다. macOS의 security 명령, Linux의 secret-tool(libsecret)을 쓰면 된다.
이렇게 하면 인증 토큰조차 파일 시스템에 평문으로 존재하지 않는다. Keychain에 접근하려면 시스템 인증(지문, 비밀번호)이 필요하다. 인증코드처럼 한 번 셋팅하면 Keychain이 안전하게 보관한다.
전체 흐름 정리
[시크릿 저장소] [인증 게이트] [런타임]
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ 1Password │────▶│ Touch ID / │────▶│ 환경변수로 │
│ or Vault │ │ 비밀번호 │ │ 프로세스에 │
│ │ │ │ │ 주입 │
└──────────────┘ └──────────────┘ └──────────────┘
│
AI는 이 값을
파일에서 읽는 게
아니라 환경변수로
받을 뿐
| .env 파일 | 1Password / Vault | |
|---|---|---|
| 저장 형태 | 평문 텍스트 | 암호화된 저장소 |
| 접근 제어 | 파일 권한뿐 | 생체인증 / 토큰 |
| 유출 시 영향 | 키 수동 로테이션 필요 | 토큰 TTL 만료 / 즉시 revoke |
| AI 서버 전송 | 파일 읽으면 전송됨 | 경로 참조만 전송 |
| 악성코드 대응 | 무방비 | 인증 없이 접근 불가 |
Claude Code에서의 실전 적용
Claude Code를 쓸 때 구체적으로 어떻게 적용하는지 보자.
CLAUDE.md에 규칙 정의
## 시크릿 관리 규칙
- .env 파일을 직접 읽지 말 것
- 시크릿이 필요하면 `op read` 또는 `vault kv get` 명령을 사용할 것
- API 키를 프롬프트에 직접 입력하지 말 것
- 시크릿이 포함된 파일을 cat, read 하지 말 것실행 예시
# 1Password 활용 (개인)
op run --env-file=.env.tpl -- claude
# Vault 활용 (조직)
export VAULT_TOKEN=$(security find-generic-password -a "vault" -s "token" -w)
OPENAI_API_KEY=$(vault kv get -field=openai secret/myapp/keys) claude이렇게 하면 Claude Code 프로세스는 환경변수로 시크릿을 받되, 파일 시스템에 평문 .env가 존재하지 않는다. AI가 파일을 탐색해도 시크릿을 찾을 수 없다.
결론
시크릿 관리의 핵심은 간단하다.
- 평문 파일에 시크릿을 두지 않는다 —
.env는 이제 악성코드의 1순위 타겟이다 - 시크릿 전용 저장소를 쓴다 — 개인은 1Password, 조직은 Vault
- 런타임에만 주입한다 — CLI를 통해 실행 시점에 꺼내온다
- 내가 승인할 때만 접근 가능하게 한다 — 지문, 비밀번호, 토큰 TTL
AI 도구가 점점 더 많은 것을 할 수 있게 되면서, 시크릿 관리의 중요성도 같이 올라가고 있다. AI에게 코드를 짜달라고 할 수는 있지만, 내 시크릿까지 통째로 넘길 필요는 없다.
시크릿은 금고에, 열쇠는 내 손에.