전체 가이드
심각 심각도노출된 시크릿

클라이언트 측 JavaScript에 노출된 API 키

브라우저로 전달되는 것 — 인라인 스크립트, 번들된 JS, source map — 은 무엇이든 누구나 읽을 수 있습니다. 프런트엔드 코드에 포함된 시크릿 키(OpenAI, Stripe 시크릿, AWS, 데이터베이스 URL)는 사실상 공개된 것입니다.

무엇인가요

빌드 도구는 컴파일 시점에 환경 변수를 인라인합니다. 클라이언트 코드에서 시크릿을 참조하면, 모든 방문자가 다운로드하는 JavaScript 번들에 그 값이 들어가게 됩니다.

중요한 구분이 있습니다: publishable/anon 키(Stripe pk_live, Firebase web config)는 공개되도록 설계되었지만, 시크릿 키(sk_live, service-role 키, 제공업체 API 키)는 그렇지 않으며 절대 브라우저에 도달해서는 안 됩니다.

왜 중요한가요

유출된 제공업체 키는 누구나 여러분의 계정으로 사용량을 쌓게 합니다 — LLM 요금, SMS 요금, 클라우드 비용 — 또는 키가 허용하는 데이터에 접근하게 합니다.

공격자는 키 패턴을 찾으려 대규모로 JavaScript 번들을 수집합니다. 커밋된 시크릿은 보통 배포된 지 몇 분 안에 악용됩니다.

고치는 방법

시크릿을 서버에 두기

서드파티 API를 브라우저가 아니라 서버 route나 백엔드에서 호출하세요. 브라우저는 여러분의 endpoint와 통신하고, endpoint가 키를 보유합니다.

// app/api/summarize/route.ts (server-only)
export async function POST(req: Request) {
  const { text } = await req.json();
  const r = await fetch("https://api.openai.com/v1/responses", {
    method: "POST",
    headers: { Authorization: `Bearer ${process.env.OPENAI_API_KEY}` },
    body: JSON.stringify({ model: "gpt-4o-mini", input: text }),
  });
  return Response.json(await r.json());
}
NEXT_PUBLIC_ 접두사를 신중히 사용하기

Next.js에서는 NEXT_PUBLIC_ 접두사가 붙은 변수만 브라우저에 노출됩니다. 그 접두사 뒤에 시크릿을 절대 두지 말고, 공개해도 안전한 값에만 사용하세요.

유출된 키 교체하기

시크릿이 브라우저로 전달됐다면 이를 폐기하고 재발급하세요. 다음 빌드에서 제거해도 이미 수집된 사본은 무효화되지 않습니다.

자주 묻는 질문

프런트엔드의 Stripe publishable 키(pk_live)가 문제인가요?

아니요 — publishable 키는 공개되도록 만들어졌습니다. 위험한 것은 서버 측에 있어야 하는 시크릿 키(sk_live)입니다.

키를 그냥 난독화하면 안 되나요?

안 됩니다. 난독화는 손쉽게 되돌릴 수 있습니다. 유일한 해결책은 시크릿을 클라이언트에서 완전히 배제하는 것입니다.

내 앱도 영향을 받나요?

AppSafe는 이 문제와 수십 가지 다른 문제를 한 번의 무료 스캔으로 점검합니다.

내 앱 무료 스캔