[緊急] vm2のサンドボックスを突破する深刻な脆弱性 (GHSA-c48m-32m9-vx93) の解説と対策
はじめに:Node.jsアプリケーションにおけるサンドボックスの重要性
Node.js環境でユーザー提供のスクリプトや信頼できないコードを実行する際、セキュリティを確保するためにサンドボックスは不可欠です。多くのプロジェクトで利用されているvm2は、Node.js内で安全にJavaScriptコードを実行するための強力なサンドボックスライブラリとして知られています。しかし、この度vm2にCriticalレベルの脆弱性 (GHSA-c48m-32m9-vx93 / CVE-2026-92951) が報告されました。この脆弱性が悪用されると、サンドボックスの境界を突破し、ホスト環境で任意のコードが実行される可能性があります。本記事では、この脆弱性の詳細、影響を受ける条件、そしてフロントエンドエンジニアとして行うべき対策について解説します。
vm2サンドボックス脆弱性 (GHSA-c48m-32m9-vx93) の概要
この脆弱性は、vm2の`NodeVM`機能において、外部パッケージの許可リスト(`external` allowlist)とカスタムのモジュール解決コールバック(`require.resolve`)を併用している場合に発生します。サンドボックス内のコードが外部パッケージを要求する際、vm2が許可リストに含まれるパッケージ名を不完全な方法(部分一致)でチェックするため、意図しない外部パッケージがロードされてしまうという問題です。
これにより、攻撃者はサンドボックスの制約を回避し、ホスト環境で任意のコードを実行できる可能性があります。これは、サンドボックスの最も基本的なセキュリティ保証を損なうものであり、非常に危険な状態と言えるでしょう。
脆弱性の詳細:なぜサンドボックスを迂回できるのか?
通常、`NodeVM`は`require.external`設定により、サンドボックス内のコードがロードできる外部npmパッケージを厳しく制限できます。また、`require.resolve`を使って、独自のパッケージ解決ロジックを定義することも可能です。しかし、問題はこれら二つの機能が連携する部分にありました。
vm2は、カスタムリゾルバーが呼び出される前に、`external` allowlistに指定されたパッケージ名が要求されたパッケージ名と一致するかどうかをチェックします。このチェックで生成される正規表現が、パッケージ名の「境界」に固定されていなかったため、部分文字列の一致で許可されてしまうというロジックの不備がありました。
具体例を見てみましょう。もし許可リストに`['left-pad']`のみが指定されていたとします。vm2は`/left-pad/`のような正規表現でチェックを行うため、`evil-left-pad`や`left-pad-backdoor`といった名前のパッケージも、「left-pad」という文字列を含んでいるため、この事前チェックを誤って通過してしまいます。
事前チェックを通過した後、vm2はアプリケーションが設定したカスタムリゾルバーを呼び出します。もしこのカスタムリゾルバーが、攻撃者が意図したパッケージ(例:`evil-left-pad`)をホスト環境で解決可能なパスから見つけてしまうと、vm2はそのパッケージをロード可能なリストに追加します。さらに、`context: 'host'`モードで実行されている場合、ホスト側の`require()`を使ってそのパッケージをロードしてしまいます。結果として、サンドボックス内の制約(組み込みモジュールの禁止など)を無視して、そのパッケージのトップレベルコードがホスト環境で実行されてしまう、というのがこの脆弱性のメカニズムです。
影響を受ける環境と潜在的なリスク
この脆弱性が悪用される主な前提条件は以下の通りです。
1. アプリケーションがvm2の`NodeVM`を使用し、信頼できない、または信頼度が低いJavaScriptコードを実行している。<br>2. `require.external` allowlistを設定しており、すべての外部パッケージを許可しているわけではない。<br>3. カスタムの`require.resolve`を設定している。<br>4. 攻撃者が意図する「衝突するパッケージ」(例:`evil-left-pad`)が、そのカスタムリゾルバーが解決できるパスにすでに存在しているか、アプリケーションの依存関係管理ワークフローなどによってそのパスに配置される可能性がある。
上記4つの条件が揃った場合、攻撃者はホストのNode.jsプロセスで任意のコードを実行できてしまいます。これにより、ホストプロセスがアクセス可能なファイルや環境変数、秘密情報の読み取り、ホスト上のデータの改ざん、またはホストサービスの停止などが可能になります。特に、ユーザーがプラグインやスクリプトをアップロードできるようなシステムでは、このリスクは甚大です。
ただし、この脆弱性はvm2が自動的に悪意のあるパッケージをnpmからダウンロードするわけではない点に注意してください。あくまで、攻撃者が用意したパッケージが、ホスト上の解決可能なパスに存在することが悪用の前提となります。
今すぐ行うべき対応策
最も確実で推奨される対応策は、vm2ライブラリを脆弱性が修正された最新バージョンに速やかにアップデートすることです。開発元は既にこの問題を修正したバージョンをリリースしているはずですので、ご自身のプロジェクトの依存関係を確認し、最新版への更新を計画してください。
もしすぐにアップデートが難しい場合でも、以下の点を確認し、対策を検討してください。
1. **`require.external` allowlistの見直し**: パッケージ名のチェックが、部分一致ではなく完全一致で行われるように設定を見直すか、最新バージョンへのアップデートが完了するまで、必要最低限のパッケージのみを許可し、特に注意が必要なパッケージ名は明示的に拒否するなどの一時的な厳格化を検討してください。<br>2. **カスタム`require.resolve`の厳格化**: カスタムリゾルバーが参照するパスに、信頼できないパッケージや、意図しない衝突を引き起こす可能性のあるパッケージが配置されないよう、厳格なアクセス制御や入力検証を導入してください。特に、プラグインアップロードディレクトリやユーザーが制御可能な依存関係ディレクトリなどを通じて、攻撃者がパッケージファイルを配置できる環境ではないかを確認してください。
この脆弱性はサンドボックスのセキュリティ境界を突破する可能性があるため、速やかにシステムへの影響を確認し、適切な対策を講じることを強く推奨します。
まとめ
Node.jsアプリケーションにおいて、信頼できないコードを安全に実行するためにvm2のようなサンドボックスライブラリは不可欠です。しかし、今回のようなCriticalな脆弱性が発見されることもあります。今回のGHSA-c48m-32m9-vx93 / CVE-2026-92951は、サンドボックスの根本的な機能を脅かすものであるため、迅速な対応が求められます。
常に利用しているライブラリの脆弱性情報をキャッチアップし、適切なバージョンアップや設定の見直しを行うことが、安全なアプリケーション開発には不可欠です。この機会に、ご自身のプロジェクトにおけるセキュリティプラクティスを再確認し、より堅牢なシステム構築を目指しましょう。