민감 정보 노출

Sensitive Information Exposure

설명

Webpack DefinePlugin에서 process.env 전체를 직렬화해 정의하면, 해당 참조를 치환하는 과정에서 비밀 값까지 클라이언트 번들에 포함될 수 있습니다. 실제 포함 여부는 참조 코드와 최적화에 따라 달라지지만, 빌드 환경 전체를 공개 대상으로 삼으면 안 됩니다. 프론트엔드 번들은 누구나 내려받을 수 있으므로, API 키·토큰·비밀값이 그대로 노출될 수 있습니다. 공격자는 배포된 JS 파일(또는 source map)을 열람하거나, 코드 내에서 노출된 값들을 검색해 비밀정보를 수집할 수 있습니다.

잠재적 영향

  • 자격 증명/API 키 노출: 번들에 포함된 키·토큰을 누구나 뷰-소스/DevTools로 확인 가능.
  • 백엔드 무단 접근: 노출된 자격정보로 API 호출, 데이터 조회·변조 시도.
  • 계정 남용 및 과금 피해: 제3자 서비스 키 악용으로 트래픽 유발, 비용 증가.
  • 규정 위반 및 평판 하락: 개인정보/보안정책 위반으로 법적 제재 및 신뢰도 하락.

해결 방법

  • DefinePlugin에 process.env 전체를 전달하지 말고, 공개가 허용된 소수의 키만 허용 목록으로 명시해 주입하세요.
  • 서버 전용 Secret은 절대 클라이언트로 노출하지 마세요. 공개 허용 변수에 PUBLIC_ 같은 접두사를 부여해 구분하는 정책을 사용하세요.
  • .env 파일을 용도별로 분리(.env.server, .env.client, .env.production 등)하고, CI/CD에서 클라이언트 빌드에 필요한 공개 변수만 export하세요.
  • webpack DefinePlugin에서 개별 키를 정의하거나 EnvironmentPlugin(['NODE_ENV','PUBLIC_API_BASE'])처럼 지정된 목록만 허용하세요.

예시

변경 전

javascript
// webpack.config.js (취약)
const { DefinePlugin } = require("webpack");

// 전체 환경을 치환 값으로 정의 -> 참조 코드에 민감정보가 포함될 수 있음
const exposeAll = { "process.env": JSON.stringify(process.env) };

module.exports = {
  mode: "production",
  plugins: [new DefinePlugin(exposeAll)],
};

변경 후

javascript
// webpack.config.js (안전)
const { DefinePlugin, EnvironmentPlugin } = require("webpack");
require("dotenv").config();

// 공개가 허용된 키만 화이트리스트로 노출 (예: PUBLIC_ 접두사)
const PUBLIC_VARS = {
  "process.env.NODE_ENV": JSON.stringify(process.env.NODE_ENV),
  "process.env.APP_VERSION": JSON.stringify(require("./package.json").version),
  "process.env.PUBLIC_API_BASE": JSON.stringify(process.env.PUBLIC_API_BASE),
};

module.exports = {
  mode: "production",
  plugins: [
    new DefinePlugin(PUBLIC_VARS),
    // 또는 필요한 키만 명시적으로 허용
    // new EnvironmentPlugin(['NODE_ENV', 'PUBLIC_API_BASE'])
  ],
};

설명:

  • 변경 전: process.env 전체를 직렬화하여 번들에 포함하면, 원래 서버 전용이어야 할 비밀값(예: API 키, 액세스 토큰, DB 연결 정보)이 공개 아티팩트에 남습니다. 사용자는 배포된 JS 파일이나 source map을 열람하여 해당 값을 쉽게 획득할 수 있습니다.
  • 변경 후: DefinePlugin으로 공개가 허용된 변수만 개별적으로 정의해 노출 범위를 최소화합니다. PUBLIC_ 등 접두사 정책을 통해 클라이언트로 내보낼 변수만 선택하고, 나머지 Secret은 서버에서만 사용하도록 분리합니다. 접두사 자체가 비밀 값을 보호하지는 않으므로 선택한 값이 실제로 공개 가능한지 검토하고, 다른 번들·source map 경로의 노출도 확인하세요.

참조