본문으로 건너뛰기
NEW · Blast Radius: 유출된 API 키 대응 플레이북 (AWS·GCP·Azure)
블로그로 돌아가기

Vercel 시크릿 노출: Next.js 브라우저 번들 확인법

Next.js 환경 변수가 브라우저로 나가는 경로를 확인하고 Stripe 공개 키와 비밀 키를 구분해 노출된 키를 교체하는 순서입니다.

편집 기록: 이전 글의 사이트별 집계는 원자료를 확인할 수 없어 제거했습니다. 이 글은 공식 문서에 근거한 점검 절차입니다.

김동현
작성자
김동현
발행
수정
7 분693 어절
Share:
환경 변수 값이 서버에 남거나 공개 클라이언트 번들로 들어가는 경계를 보여주는 도식

2026년 10월 4일 수정. 이전 글은 사이트별 유출률을 자체 조사 결과로 제시했습니다. 표본 목록, 결과 기록, 검증 절차를 찾지 못해 이 주장을 제거했습니다. 이 글은 다른 사람의 키를 시험하지 않고도 사이트 담당자가 직접 재현할 수 있는 노출 경로와 확인 방법을 다룹니다.

Vercel 환경 변수가 브라우저에 보일 수 있나요?

애플리케이션 코드가 그 값을 브라우저로 보내면 보일 수 있습니다. Vercel에 환경 변수를 저장하는 것만으로 공개되지는 않습니다. Next.js는 클라이언트 코드에서 직접 참조한 NEXT_PUBLIC_ 변수 값을 빌드할 때 JavaScript에 넣습니다. next.config.js의 env 값도 번들에 포함됩니다. 서버 컴포넌트나 라우트가 값을 HTML·JSON·속성으로 반환해 새는 경로도 있습니다. 저장 설정뿐 아니라 코드의 전달 경로와 실제 빌드 결과를 확인해야 합니다.

.env 파일을 Git에 커밋하는 것은 별개의 노출 경로입니다. 클라이언트 번들에 값이 없어도 저장소를 읽을 수 있는 사람에게 키가 보일 수 있습니다. 로그, 복사된 빌드 산출물, 이전 공개 배포에도 남을 수 있습니다. 자신에게 접근 권한이 있는 범위에서 각각 확인합니다.

Stripe 키 중 무엇을 클라이언트에 둘 수 있나요?

pk_로 시작하는 Stripe 게시 가능 키는 클라이언트 사용을 위해 만들어졌습니다. sk_ 비밀 키와 rk_ 제한 키는 서버에 보관해야 합니다. sk_live_ 값이 공개 번들에 있다면 페이지가 정상 동작하고 악용 기록이 없어도 노출된 것으로 다뤄야 합니다. 접두어만으로 전체 문자열이 유효한 키라고 단정하지 말고 발급처에서 상태와 권한을 확인합니다.

빌드 과정에서 시크릿이 공개되는 예

아래는 작동하지 않는 예시 값입니다. 클라이언트 코드에서 참조한 NEXT_PUBLIC_ 변수는 Next.js가 브라우저용 코드에 넣을 수 있습니다. 서버 전용 변수로 옮겨 서버 라우트에서만 사용하면 의도한 클라이언트 코드 경로에서 값이 빠집니다. 렌더링된 HTML과 응답도 확인해야 합니다.

typescript
// 위험: 브라우저 코드에서 참조
const secret = process.env.NEXT_PUBLIC_STRIPE_SECRET;

// 서버 라우트에서만 값 사용
import 'server-only';
const stripeSecret = process.env.STRIPE_SECRET_KEY;

변수 이름만으로 노출 여부가 결정되지는 않습니다. Next.js 문서에서 빌드 시 치환되는 것은 직접 참조한 NEXT_PUBLIC_ 변수이며 동적 조회는 다르게 처리됩니다. 반대로 서버 전용 이름을 쓰더라도 응답 본문에 값을 실으면 노출됩니다. 코드 검색과 함께 실제 운영 빌드와 전달되는 페이지를 시험합니다.

내 사이트에서 확인할 순서

1. 값이 밖으로 나갈 수 있는 경로 찾기

NEXT_PUBLIC_ 참조, next.config.js env, 클라이언트 컴포넌트, 서버에서 클라이언트로 넘기는 속성, HTML, API 응답, 소스맵, 공개 정적 파일을 살핍니다. .env 파일이 Git에 커밋됐는지도 확인합니다. 정확한 키 값을 티켓이나 CI 로그에 복사하지 말고 파일 경로, 변수 이름, 일부 가린 식별자만 기록합니다.

2. 새 운영 빌드의 결과 확인하기

자신의 빌드를 로컬에서 만들고 클라이언트 산출물에 발급처별 비밀 키 접두어가 있는지 찾습니다. 아래 명령은 파일 이름만 출력합니다. 접두어 검색에서 아무것도 나오지 않아도 검사가 끝난 것은 아닙니다. 발급처마다 형식이 다르고 값이 변형됐거나 .next/static 밖의 HTML·JSON에 있을 수 있습니다.

bash
# 직접 만든 Next.js 빌드 산출물에서만 실행
rg -l 'sk_live_|rk_live_' .next/static

3. 방문자가 닿을 수 있었던 배포 확인하기

영향받은 빌드와 현재 운영, 프리뷰, 직접 관리하는 이전 배포 URL을 비교합니다. Next.js 공개 변수는 빌드에 고정되고 Vercel의 환경 변수 변경은 새 배포에만 적용됩니다. 대시보드에서 값만 바꿔서는 이전 클라이언트 번들에 남은 키를 제거할 수 없습니다.

비밀 키가 공개됐다면 무엇부터 바꾸나요?

담당자가 노출된 값이 현재 작동하는 비밀 키인지 공개용 키인지 발급처에서 확인합니다. 살아 있는 비밀 키라면 대체 키를 준비하고 종속 환경을 갱신해 배포·시험한 뒤 기존 키를 신속히 폐기합니다. 악용이 의심되거나 즉각 차단이 필요하면 폐기를 앞당기고 서비스 복구를 함께 조율합니다. 순서는 발급처의 기능과 중단 허용 범위에 따라 달라집니다. 가능한 기간의 발급처 활동 로그도 검토합니다. 기록이 없다고 미사용을 입증할 수는 없습니다.

값을 밖으로 보낸 코드 경로를 없애고 다시 빌드합니다. 운영·프리뷰·아직 열리는 이전 배포를 확인합니다. Git 기록에도 키가 있다면 먼저 발급처에서 폐기한 뒤 기록 정리를 조율합니다. 기록을 다시 써도 이미 복제된 사본까지 지울 수는 없습니다. Vercel의 민감 변수 설정은 대시보드에서 저장된 값을 읽는 범위를 제한하지만, 코드가 브라우저로 내보낸 값을 되돌리지는 못합니다.

Cremit은 무엇을 확인할 수 있나요?

Cremit은 지원하는 연결 소스에서 자격증명을 찾고, 지원 유형을 발급처에 확인하며, 제공 가능한 AWS 액세스 키·GCP API 키 정보를 보여주고 담당자의 조치를 추적할 수 있습니다. 모든 Vercel 배포나 브라우저 번들을 검사했다고 입증하거나 모든 Stripe 키의 현재 권한을 보여주거나 키를 자동 교체하지는 않습니다. 배포와 발급처 기록으로 그 빈틈을 확인합니다.

조치 완료를 뒷받침할 기록

영향받은 변수와 빌드 식별자, 첫·마지막 공개 배포, 노출 사본 위치, 발급처의 키 상태, 담당자, 대체·폐기 시각, 종속 서비스 시험, 로그 검토 기간을 남깁니다. 최초 노출 시점이나 이미 내려받은 사본을 알 수 없다면 그대로 적습니다. 새 키로 서비스가 작동하고 이전 키는 발급처에서 인증에 실패하는지 확인해야 조치를 검증할 수 있습니다.

함께 읽을 글

시크릿 스캐닝: 패턴 일치와 작동하는 키 구분하기

프런트엔드 번들에서 자격증명이 새는 다른 경로

근거 자료

Next.js — 환경 변수와 클라이언트 번들

Next.js — next.config.js env의 번들 포함

Vercel — 환경 변수와 배포

Vercel — 환경 변수 시크릿 교체

Vercel — 민감 환경 변수

Stripe — API 키 종류와 교체

네트워크에 공유해보세요LinkedInX

이어서 읽기

다음 글을 메일로 받아보세요

Cremit의 월간 자격증명 보안 브리프. 한 통에 핵심만 담습니다.

이메일을 판매하거나 외부에 넘기지 않으며, 언제든 수신거부할 수 있습니다.