node_modulesの静的配信による情報漏えい

情報漏えい

説明

Expressの express.static() で node_modules 全体を公開すると、package.json、ソースマップ(.map)、README、テストデータなどの内部ファイルを直接取得される場合があります。package.json の _where のようなメタデータが、ローカルの絶対パスやユーザー名を含むこともあります。攻撃者が /vendor/<pkg>/package.json や /vendor/<pkg>/dist/<file>.map などを推測し、公開された依存関係のバージョンから脆弱性を探す手がかりにする可能性があります。

想定される影響

  • パッケージのメタデータからの内部パス、ユーザー名、プロジェクト構造の漏えい
  • 後続の攻撃に利用できる依存関係のバージョンや設定の公開
  • ソースマップや意図しないファイルによる、コード、処理内容、秘密情報に関する手がかりの露出
  • ほかの公開パスや設定ミスの発見が容易になること

対処方法

  • node_modules 全体を express.static() で公開しないでください。
  • 必要なパッケージの dist、browser、umd など、公開用の下位ディレクトリだけを許可リストで指定してください。
  • Webpack、Vite、Rollupなどのバンドラーやビルドスクリプトで、必要なアセットだけを専用の public ディレクトリへコピーし、そのディレクトリだけを配信してください。
  • 不要なソースマップ、テスト、ドキュメントのファイルはデプロイ対象から除外してください。
  • 静的配信のルートを確認し、serve-index などによる不要な一覧表示を避け、公開用の専用ディレクトリに限定してください。

例

変更前

javascript
const express = require('express');
const path = require('path');

const app = express();

// 変更前: node_modules全体を配信し、内部ファイルも公開します。
app.use('/vendor', express.static(path.join(__dirname, 'node_modules')));

app.listen(3000, () => console.log('listening on 3000'));

変更後

javascript
const express = require('express');
const path = require('path');

const app = express();

// 変更後: 必要なパッケージの公開用下位ディレクトリだけを配信します。
app.use('/vendor/jquery', express.static(path.join(__dirname, 'node_modules', 'jquery', 'dist')));
app.use('/assets', express.static(path.join(__dirname, 'public'))); // ビルドまたはコピーした公開用アセットだけを配信します。

app.listen(3000, () => console.log('listening on 3000'));

説明:

  • 変更前: ルート全体を公開すると、メタデータ、ソースマップ、サンプルやテストのファイルへ直接アクセスできます。内部パス、バージョン、ソース構造が攻撃の手がかりになるおそれがあります。
  • 変更後: 公開を意図したファイルだけに配信対象を絞ります。ビルド時に必要なアセットを public にコピーすれば、node_modules の内部構造やメタデータの露出も避けられます。

参考資料