.env는 매우 편리해 거의 모든 현대 프레임워크가 사용합니다. 하지만 한 번 잘못된 곳에 두면 그 안의 모든 시크릿을 누구나 읽을 수 있게 됩니다. 이것이 무엇인지, 그리고 어떻게 지키는지를 바닥부터 살펴봅니다.
조금 더 자세히
앱에는 "환경마다 바뀌는 값"과 "아무에게도 보이고 싶지 않은 값"이 있습니다: DB 비밀번호, 외부 API 키, 세션을 암호화하는 키. 이것들을 소스에 하드코딩하면 코드를 공유하는 순간 시크릿이 새어 나갑니다.
그래서 이를 하나의 .env 파일에 모아 코드에서 "환경 변수"로 읽습니다. Next.js, Laravel, Rails 등 많은 프레임워크가 이 방식으로 동작합니다.
내용은 한 줄에 '이름=값' 하나씩 쓰는 일반 텍스트입니다(아래 값은 자리 표시용입니다).
APP_ENV=production
DB_HOST=127.0.0.1
DB_PASSWORD=your-db-password-here
PAYMENT_API_KEY=your-payment-api-key-here설정을 코드 밖에 두는 방식은 웹 앱 설계 지침 "The Twelve-Factor App"으로 널리 퍼졌습니다. 이 지침은 설정이 제대로 분리되었는지 판단하는 기준으로, 자격 증명을 하나도 노출하지 않고 지금 당장 코드베이스를 오픈소스로 공개할 수 있는지를 듭니다. .env는 개발 중에 이 생각을 실천하기 위한 편리한 그릇입니다.
비슷한 것과의 차이
.env와 자주 헷갈리는 것들입니다. 이 중 일부만이 시크릿을 두어도 되는 곳입니다.
| 이름 | 무엇인가 | 시크릿을 두어도 되나 |
|---|---|---|
| 환경 변수 | OS나 컨테이너가 실행 중인 프로세스에 넘기는 값 | 된다(운영 환경의 기본) |
.env | 환경 변수를 파일로 모은 것 | 되지만, 공개 디렉터리나 Git에는 절대 두지 않는다 |
.env.example | 이름만 나열하고 값은 비운 견본 | 안 된다(커밋하기 위한 파일) |
NEXT_PUBLIC_* / VITE_* | 브라우저용 JavaScript에 박혀 들어가는 변수 | 안 된다(누구나 읽을 수 있음) |
| 시크릿 매니저 | 비밀 값을 보관하는 클라우드 서비스 | 된다. 권한과 감사 로그로 관리 |
놓치기 쉬운 것은 네 번째 줄입니다. Next.js는 NEXT_PUBLIC_로, Vite는 VITE_로 시작하는 변수의 값을 빌드할 때 브라우저로 보내는 JavaScript에 박아 넣습니다. .env 안에 있더라도 이 접두어가 붙은 변수는 공개됩니다.
왜 "가장 위험한 파일"인가
여러 키가 한 파일에 있기 때문에, 한 번의 노출이 피해를 빠르게 번지게 합니다. "유출된 각 값이 무엇을 여는지" 나열하면 심각성이 분명해집니다.
| 유출된 값 | 무슨 일이 일어나는가 |
|---|---|
| DB 자격 증명 | 데이터 읽기 / 변조 / 삭제 |
| 외부 API 키 | 사칭 사용, 부정 과금 |
암호화 키(예: Laravel APP_KEY, 서명 키) | 세션 위조, 사칭 로그인 |
| OAuth 시크릿 | 연동 앱 장악 |
실제로 .env 노출은 고전적인 사고입니다. → 전 세계에 노출된 .env
실제 사례: 노출된 .env가 노려진다
- 대규모 갈취 작전(2024): 한 보안 기업의 연구팀은 11만 개 도메인을 노려 웹 서버에 노출된
.env파일을 수집하고, 그 안의 클라우드 인증 정보로 데이터를 빼낸 뒤 몸값을 요구한 활동을 보고했습니다. 수집된 변수는 9만 개가 넘었고, 그중 약 7,000개가 클라우드 서비스용, 약 1,500개가 SNS 계정용이었습니다. 원인은 클라우드 사업자의 취약점이 아니라,.env를 노출한 조직 쪽의 설정 실수였습니다 - Vercel(2026년 4월): 내부 시스템에 대한 무단 접근으로 일부 고객의 환경 변수가 읽혔습니다. 값을 다시 표시할 수 있는 분류로 저장된 것들이었습니다 → 상세 기사
- 공개 저장소(2026년 연구): GitHub에 공개된 54만 건이 넘는 인증 정보가 조사 시점에도 여전히 유효했습니다. 인증 정보를 폐기하지 않으면 파일이나 이력을 지워도 소용이 없습니다 → 상세 기사
첫 번째 사례는 노출된 .env가 사람이 둘러보다가 발견하는 것이 아니라, 자동으로 대량 수집된다는 것을 보여 줍니다. 작은 사이트라서 발견되지 않을 것이라고 가정하지 마세요.
전형적인 유출 = 배치 실수
가장 큰 원인은 "앱 전체를 공개 디렉터리(문서 루트)에 두는 것"입니다. public/만 서빙되어야 할 곳에서 그 상위를 노출하면 .env까지 닿을 수 있게 됩니다.
✗ 위험(앱이 공개 루트에 있음)
public_html/ ├─ .env ← 읽힘! ├─ app/ └─ public/
✓ 안전(앱은 바깥, public만 서빙)
app-root/ ├─ .env ← 닿을 수 없음 ├─ app/ └─ public_html/ → public/
배치 외에도 다음 경로로 유출됩니다.
- Git에 커밋한다(공개 저장소라면 즉시 공개, 비공개라도 접근 권한이 있는 모두에게 보임)
.env.bak,.env.old같은 백업을 공개 디렉터리에 남겨 둔다.env가 든 채로 컨테이너 이미지나 배포용 zip을 내보낸다- 위에서 설명한 대로
NEXT_PUBLIC_/VITE_변수에 시크릿을 넣는다
지키는 법
공개 디렉터리에 두지 않기
앱을 문서 루트 바깥에 두고 public/만 서빙하세요. 공유 호스팅의 구체적 절차: 이 가이드.
git에 커밋하지 않기
.env*를 .gitignore에 추가하고, 값이 비어 있는 .env.example만 공유하세요.
유출되면 전부 교체
확인된 키 하나만이 아니라 — .env에 있던 모든 키를 교체하세요. 전부 보였다고 가정하고 움직이세요.
정기적으로 자가 점검
자기 사이트에서 /.env를 열 수 없는지 확인하는 습관을 들이세요.
사이트와 저장소에서 확인하는 방법
자가 점검(본인 소유 도메인에 대해서만)
# 본문이 있는 200이면 노출된 것입니다. 403/404이면 지금은 괜찮습니다.
curl -sI https://yourdomain/.env | head -1
curl -sI https://yourdomain/.git/config | head -1배포할 때마다 이를 점검하면 배치 실수를 일찍 잡아냅니다.
사이트의 바깥과 저장소의 안쪽을 모두 확인하세요.
- 소유를 확인한 사이트라면 사이트 보안 점검이
/.env,/.env.local,/.env.bak같은 경로를 한 번에 검사합니다 - 저장소에서
git log --all --oneline -- .env를 실행해 커밋된 적이 있는지 확인합니다. 무언가 나오면 그 안에 있던 키를 폐기하세요 - 설정이나 코드를 시크릿 유출 스캐너에 붙여 넣어 하드코딩된 API 키나 개인 키를 찾습니다(브라우저 안에서 동작)
NEXT_PUBLIC_/VITE_변수 목록을 점검해 빌드된 JavaScript에 시크릿이 들어가지 않는지 확인합니다
본 사이트의 견해: .env를 둘 곳과 키를 폐기하는 방법을 함께 정한다
.env 사고의 대부분은 잘못된 곳에 놓인 파일 하나에서 시작됩니다. 그 뒤에 피해를 키우는 것은 키를 교체하는 절차가 정해져 있지 않다는 점입니다. 본 사이트는 .env를 쓴다면 공개 디렉터리와 Git 밖에 두는 것에 더해, 그 안의 키마다 폐기하고 재발급하는 방법을 미리 적어 두기를 권장합니다. 운영 환경에서는 클라우드 시크릿 매니저나 값을 다시 표시할 수 없는 환경 변수로 옮기면 유출 경로가 더 줄어듭니다.
출처(1차 자료)
- The Twelve-Factor App, "III. Config" — 12factor.net(설정을 환경 변수에 둔다, 오픈소스 판단 기준)
- Next.js 문서, "Environment Variables" — nextjs.org(
NEXT_PUBLIC_는 빌드 시 JavaScript에 인라인됨, .env 파일을 커밋하지 않기) - Vite 문서, "Env Variables and Modes" — vite.dev(
VITE_변수는 클라이언트 코드에 노출됨) - Unit 42(Palo Alto Networks), "Leaked Environment Variables Allow Large-Scale Extortion Operation in Cloud Environments"(2024년 8월 15일) — unit42.paloaltonetworks.com