webpack DefinePluginによる環境内の秘密情報の露出

webpack DefinePluginによる環境内の秘密情報の露出

説明

webpackのDefinePluginで process.env 全体をシリアライズした置換値として定義すると、参照の置換によって秘密値までクライアントバンドルに含まれるおそれがあります。実際に出力される値は参照コードや最適化に依存しますが、ビルド環境全体を公開データとして扱うべきではありません。JavaScriptやsource mapを取得できる人が、含まれたAPIキー、トークンなどを読み取る可能性があります。

想定される影響

  • 公開アーティファクト内の認証情報やAPIキーを取得されるおそれがあります。
  • 漏えいした認証情報でバックエンドAPIを呼び出されたり、データへ不正アクセスされたりする可能性があります。
  • 外部サービスのキーを悪用され、通信量や料金が増えるおそれがあります。
  • プライバシーやセキュリティ要件への違反、信頼の低下につながる可能性があります。

対処方法

  • 環境全体をDefinePluginへ渡さず、公開を承認した変数だけを明示的に定義してください。
  • サーバー専用の秘密値をクライアントコードへ渡さないでください。PUBLIC_ などの命名規則は区別に役立ちますが、値を保護するものではありません。
  • 用途ごとに環境ファイルを分け、CI/CDのクライアントビルドへ必要な公開変数だけを供給してください。
  • 個別のキーを定義するか、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'])
  ],
};

解説:

  • 変更前: 環境全体をシリアライズした置換値により、サーバーの秘密情報が公開JavaScriptやsource mapへ残る可能性があります。
  • 変更後: 公開を承認した変数だけを個別に定義します。選択した値が実際に公開可能か、別のバンドルやsource map経由の露出がないかも確認してください。PUBLIC_ という接頭辞だけでは漏えいを防げません。

参考資料