.env 유출이라고 하면 대부분 "실수로 git add . 하고 커밋한 경우"를 떠올립니다. 그건 이제 .gitignore와 pre-commit 훅으로 상당 부분 막힙니다. 진짜 문제는 새로 열린 경로입니다. Cursor·Claude Code 같은 AI 코딩 도구는 여러분을 돕기 위해 저장소를 넓게 읽습니다. 바로 그 "넓게 읽는" 편의가 .env가 새는 길이 됩니다.
이 글은 개발자 시점에서 씁니다. 완곡어법 없이, 실제 .env·설정·MCP config가 어떻게 모델로 흘러가는지 세 경로로 나눠 보고, 각각을 실무에서 어떻게 막는지 코드·설정 예시와 함께 정리합니다. (예시의 시크릿은 전부 sk-test-0000… 같은 명백한 더미입니다.)
.env는 세 곳에서 샌다: ① 모델에 딸려가는 repo context, ② 채팅에 붙여넣는 로그·설정, ③ MCP filesystem 서버의 넓은 접근. 방어의 핵심은 "AI를 끄는 것"이 아니라 컨텍스트 경계와 secret 스캐닝을 세우는 것이다.
.env는 왜 AI 시대에 더 위험해졌나
.env 한 줄에는 보통 DB 접속 문자열, 서드파티 API 키, OAuth 시크릿, 서명 키가 들어갑니다. 이건 즉시 악용 가능한 실탄입니다. 과거엔 이게 유출되는 경로가 "커밋"과 "로그"로 비교적 명확했습니다. 그래서 방어도 명확했습니다.
AI 코딩 도구는 이 그림을 바꿉니다. 도구가 여러분의 의도와 무관하게 주변 파일을 컨텍스트로 끌어오고, 그 컨텍스트는 원격 모델로 전송됩니다. 여러분이 .env를 "붙여넣지 않아도" 도구가 대신 읽어 보낼 수 있다는 뜻입니다.

.env에서 세 갈래로 샌다. 경로마다 방어 지점이 다르다.경로 ① repo context에 실려 모델로
가장 조용하고 가장 놓치기 쉬운 경로입니다. Cursor의 @Codebase·자동 컨텍스트, Claude Code의 저장소 읽기는 관련 파일을 찾아 프롬프트에 넣습니다. 이때 .env, .env.local, config/secrets.yml 같은 파일이 함께 딸려가면, 그 내용이 원격 모델로 전송됩니다.
방어의 첫 걸음은 도구가 읽지 못하도록 경계를 긋는 것입니다. 대부분의 도구는 무시 파일을 지원합니다.
# .cursorignore (프로젝트 루트)
.env
.env.*
**/secrets/**
**/*.pem
**/*.key
config/credentials*.yml
Claude Code·기타 도구도 유사한 ignore 설정을 둡니다. 여기에 더해, 애초에 시크릿을 파일이 아니라 시크릿 매니저·환경 주입으로 관리하면 파일 자체가 없어 샐 것도 없습니다.
가장 안전한.env는 디스크에 없는.env다. 도구가 읽을 파일이 없으면 컨텍스트에 실릴 일도 없다.
- ignore 파일로 AI 도구의 컨텍스트에서 시크릿 경로를 제외한다.
- 시크릿은 환경 주입/매니저로 — 커밋 트리에 평문 시크릿 파일을 두지 않는다.
- 정기 점검 — ignore가 실제로 적용되는지, 새 시크릿 파일이 예외로 새지 않는지 확인한다.
경로 ② 채팅에 로그·설정 붙여넣기
이건 개발자 스스로 하는 유출입니다. "이 에러 왜 나?"라며 스택트레이스를, "이 설정 맞아?"라며 docker-compose.yml이나 .env를 통째로 채팅에 붙여넣습니다. 그 안에 접속 문자열과 토큰이 그대로 실려 있습니다.
흔히 이런 형태입니다.
$ cat .env
DATABASE_URL=postgres://app:hunter2@db.internal:5432/prod
OPENAI_API_KEY=sk-test-0000000000000000
AWS_SECRET_ACCESS_KEY=AKIA-test-0000-EXAMPLE
이 블록을 그대로 붙여넣는 순간 DATABASE_URL의 비밀번호와 키가 외부로 나갑니다. 방어법은 습관과 도구 양쪽입니다.
- 붙여넣기 전 마스킹 습관 — 값은
<redacted>로 바꾸고 구조만 공유한다. - 입력 단계 탐지 — 브라우저·에디터에서 전송 전에
sk-,ghp_,AKIA…, 접속 문자열 패턴을 자동 감지해 마스킹하거나 경고한다. - 노출 시 즉시 로테이션 — 실수로 나갔다면 해당 키를 지체 없이 폐기·재발급한다. 유출된 키는 이미 유효하지 않다고 가정한다.
여기서 중요한 건 감시가 아니라 그 순간의 차단입니다. 무엇을 붙여넣었는지 원문을 수집·저장하는 방식은 그 자체가 또 다른 유출 지점이 됩니다. 탐지는 로컬에서, 원문은 남기지 않는 설계가 옳습니다.
경로 ③ MCP filesystem 서버의 광범위 접근
세 번째는 구조적인 문제입니다. Cursor·Claude Desktop·Claude Code에 MCP filesystem 서버를 붙이면, AI 에이전트가 지정된 경로의 파일을 직접 읽고 씁니다. 문제는 그 경로가 흔히 너무 넓다는 것입니다.
// mcp.json — 위험한 설정
{
"mcpServers": {
"filesystem": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem", "/Users/dev"]
}
}
}
루트를 /Users/dev로 열면 에이전트가 프로젝트뿐 아니라 SSH 키, 다른 저장소의 .env, 다운로드 폴더까지 읽을 수 있습니다. 프롬프트 인젝션이 한 번 성공하면 그 넓은 접근이 그대로 공격 표면이 됩니다.
방어는 접근 범위를 프로젝트로 좁히는 것입니다.
// mcp.json — 범위를 좁힌 설정
{
"mcpServers": {
"filesystem": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem", "/Users/dev/project-a"]
}
}
}
- 경로 최소화 — filesystem 서버 루트를 현재 프로젝트 폴더로만 제한한다.
- read-only 우선 — 쓰기가 꼭 필요하지 않으면 읽기 전용으로.
- 변경 감시 — 팀원 PC에 새 MCP 서버가 붙거나 경로가 넓어지면 알 수 있어야 한다.
MCP 위험 전반은 별도 가이드에서 더 깊게 다룹니다. → Claude Code·Cursor 개발팀을 위한 MCP 보안 완벽 가이드
secret 스캐닝과 PR 리스크
세 경로를 개인 습관으로만 막으면 팀 규모에서 반드시 뚫립니다. 팀 단위의 안전망은 secret 스캐닝입니다. 두 지점에 겁니다.

- pre-commit 훅 — 커밋되기 전에 로컬에서 시크릿 패턴을 스캔해 차단한다.
sk-·ghp_·AKIA…·개인키 헤더 등. - PR 게이트 — 원격에서 PR diff를 스캔해 시크릿이 발견되면 머지를 막는다. 로컬 훅을 우회한 경우의 마지막 그물.
여기에 AI가 만든 코드가 더해지면 리스크가 하나 늘어납니다. 에이전트가 생성한 PR에 하드코딩된 자격증명이나 취약 패턴이 섞일 수 있습니다. 그래서 사람이 짠 코드든 AI가 짠 코드든 동일한 secret 스캐닝·리뷰 게이트를 통과시켜야 합니다. AI PR이라고 예외를 두는 순간 그게 구멍이 됩니다.
브라우저·에디터의 입력 단계에서 시크릿 패턴을 전송 전에 탐지하고, 저장소 쪽에서는 커밋·PR의 시크릿을 스캔해 새는 지점을 막습니다. 탐지 원문은 서버로 보내지 않고 저장하지 않으며, 결과는 유형·건수 메타데이터로만 남깁니다. — Dev Repo Guard, Desktop & MCP Scanner, 개발팀 솔루션
개발팀 체크리스트
.cursorignore(및 도구별 ignore)에.env·키·시크릿 경로를 넣었는가?- 시크릿을 파일이 아니라 환경 주입·시크릿 매니저로 관리하는가?
- 채팅에 로그·설정을 붙여넣기 전에 값을 마스킹하는 습관·도구가 있는가?
- MCP filesystem 서버의 루트가 프로젝트 폴더로 좁혀져 있는가?
- 새 MCP 서버·경로 확장을 감지할 방법이 있는가?
- pre-commit 훅과 PR 게이트 양쪽에 secret 스캐닝이 걸려 있는가?
- AI가 생성한 PR도 사람 코드와 같은 리뷰·스캔 게이트를 통과하는가?
세 개 이상 "아니오"라면, 여러분 팀의 .env는 지금 이 순간에도 어딘가로 흘러가고 있을 수 있습니다.
