説明
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の内部構造やメタデータの露出も避けられます。