2026年1月末から2月にかけて、Node.jsエコシステムで広く利用されてきたサンドボックスライブラリに壊滅的な脆弱性が連続公開された。SandboxJSでは5件のCVSS 10.0級脆弱性が、vm2では非推奨後もなお残るCVSS 9.8の脱出脆弱性が報告され、プロトタイプ汚染・TOCTOU・レガシーアクセサ悪用・Promise未隔離など多様な攻撃ベクトルが実証されている。本稿ではこれらの脆弱性を技術的に分解し、n8nをはじめとするワークフローツールへの波及リスク、そしてNode.jsにおけるサンドボックスの構造的限界を論じる。

SandboxJS:2週間で5件のCVSS 10.0が露呈

SandboxJSは、JavaScriptのAST(抽象構文木)解析とES6 Proxyを組み合わせてサンドボックスを実現するライブラリである。2026年1月27日から2月8日の約2週間で、7件のGitHubセキュリティアドバイザリが公開され、うち5件がCVSS 10.0(AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H)と評価された。すべてリモートから認証不要で悪用可能であり、サンドボックス外での任意コード実行(RCE)を許す。

最初に公開されたCVE-2026-25142(2月2日)は「Ghost Accessor」と呼ばれる手法を用いる。レガシーJavaScriptの__lookupGetter__メソッドはES6 Proxyのトラップを迂回するため、サンドボックスのメンブレンを透過してホスト側のFunction参照を取得できる。取得した関数の.constructorプロパティを辿ればFunctionコンストラクタに到達し、ホストコンテキストで任意コードが実行される。ES6仕様とレガシーAPI間の論理的ギャップを突いた脆弱性であり、緊急パッチ(v0.8.27)が翌日にリリースされた。

2月6日にはさらに4件のCVSS 10.0脆弱性がNVDに同時公開された。CVE-2026-25520は関数の戻り値ラッピング不備を突く。Object.values()Array.prototype.at()を組み合わせることでホスト側のFunctionコンストラクタに直接アクセスし、サンドボックスを脱出する。CVE-2026-25586はサンドボックスオブジェクト上でhasOwnPropertyをシャドウイングすることでプロトタイプホワイトリストの強制を無効化し、__proto__経由でホストのObject.prototypeを汚染する。この汚染はサンドボックスの境界を越えて永続するため、他のサンドボックスインスタンスにも波及する。

CVE-2026-25587はMapがSAFE_PROTOTYPESに不適切に含まれている問題を悪用し、Map.prototype.hasを上書きして隔離を突破する。CVE-2026-25641は古典的なTOCTOU(Time-of-Check to Time-of-Use)競合で、プロパティキーの検証時と使用時の間に悪意あるオブジェクト変換を介入させる。これら4件はv0.8.29で修正されたが、さらに2月8日にはCVE-2026-25881(配列中間体を経由するプロトタイプ汚染、CVSS 8.3〜9.1)が追加報告され、完全な修正にはv0.8.31が必要となった。

vm2:非推奨から2年半、なお発生するCVSS 9.8脱出

vm2はNode.jsのvmモジュールをProxyでラップし、より安全なサンドボックスを提供することを目的としたライブラリである。2023年7月にCVE-2023-37466を契機として正式に非推奨となったが、npmでの週間ダウンロード数は依然100万を超え、多くのプロジェクトが依存を解消できていない。

2026年1月26日に公開されたCVE-2026-22709(CVSS 9.8)は、Promise処理におけるサニタイズの不整合を突く。vm2の内部実装ではlocalPromise.prototype.thenのコールバックは適切にサニタイズされるが、globalPromise.prototype.thenおよびglobalPromise.prototype.catchはサニタイズされていなかった。async関数はglobalPromiseオブジェクトを返すため、攻撃者はこのギャップを利用できる。

具体的な攻撃チェーンは次の通りである。まずFunction.prototype.call()をオーバーライドしてサニタイズチェックを迂回する。次にSymbolベースの型変換失敗時に生成されるエラーオブジェクトを利用し、コンストラクタチェーンを辿ってホスト側のFunctionコンストラクタに到達する。修正版(v3.10.2)ではFunction.prototype.call()Reflect.apply()に置換することで、サンドボックスコードからのインターセプトを防止した。Reflectの組み込みメソッドはサンドボックスコードから上書きできないため、原理的に同種の攻撃は成立しなくなる。

vm2の歴史を振り返ると、これまでに20件以上のサンドボックス脱出脆弱性が確認されている。メンテナ自身が「今後も新たなバイパスが発見されるだろう」と述べており、Proxyベースのユーザランドサンドボックスが信頼境界として機能しないことを事実上認めている。

n8nへの波及:ワークフローツールのRCE連鎖

サンドボックスライブラリの崩壊は、これらに依存するワークフロー自動化ツールに直接波及した。n8nはワークフロー内の式評価やカスタムコード実行にサンドボックスを使用しており、2026年1〜2月に計8件のCVEが開示されている。

CVE-2026-1470(CVSS 9.9、1月27日開示)は、式エンジンのAST検証にギャップがあり、非推奨のwith文を経由してサンドボックスを突破する。影響範囲はn8n 1.65〜1.120.4およびn8n 2.0.0〜2.4.4と広範であり、認証済みユーザがホスト上で任意コマンドを実行できる。

CVE-2026-25049(CVSS 9.4、2月開示)はTypeScriptの式サンドボックス脱出である。TypeScriptのコンパイル時型システムとJavaScriptのランタイム動作の不一致を悪用し、非文字列値(オブジェクト、配列、Symbol)をランタイムサニタイズに通過させる。さらに分割代入構文を用いてシステムレベルコマンドを実行する。公開ウェブフックでトリガー可能なため、未認証の攻撃者がワークフロー経由でRCEを達成できる点が深刻である。修正版はn8n 2.5.2以降で提供されている。

n8nの事例は、サンドボックスライブラリの脆弱性が単体の問題にとどまらず、それを組み込んだアプリケーション全体のセキュリティモデルを崩壊させることを示している。同様のリスクはn8nに限らず、サーバーサイドでユーザ提供コードを実行するあらゆるプラットフォーム──ローコードツール、テンプレートエンジン、プラグインシステム──に存在する。

Node.jsサンドボックスの構造的限界と代替アプローチ

Node.jsのvmモジュールの公式ドキュメントには「信頼できないコードの実行には使用しないこと」と明記されている。にもかかわらず、vm2やSandboxJSのようなライブラリが生まれ続けるのは、軽量なコード隔離への強い需要があるためである。しかし、これらの取り組みが繰り返し破綻する構造的理由がある。

第一に、Node.jsのvmモジュールは同一V8プロセス内で動作し、JavaScriptヒープメモリを共有する。Proxyやオブジェクトラッピングで表面的に隔離しても、レガシーAPIや言語仕様の暗黙的な動作(プロトタイプチェーン、Symbol変換、getter/setter)を網羅的に封じることは極めて困難である。今回のSandboxJS脆弱性群が示す通り、__lookupGetter__hasOwnPropertyのシャドウイング、Map.prototypeの不適切な信頼、TOCTOU競合など、攻撃面は多岐にわたる。

第二に、async/awaitとPromiseの導入がサンドボックスの複雑性を指数的に増大させている。vm2のCVE-2026-22709が示すように、ローカルPromiseとグローバルPromiseの二重性は見落としやすく、コールバックチェーンの各段階でサニタイズが必要になる。

構造的に堅牢な代替アプローチとしては、V8のIsolateインタフェースを直接利用するisolated-vmがある。各IsolateはメモリヒープとGCが分離されており、Cloudflare WorkersやDeno Deployと同じ技術基盤に立つ。ただし現在はメンテナンスモードであり、長期的なセキュリティ保証には不安が残る。

より根本的な解決策として、プロセスレベルの隔離(コンテナ、VM、OS機能)やWebAssemblyサンドボックスがある。Denoは明示的な権限システム(--deny-read--deny-net等)を備え、Node.jsより安全な実行モデルを提供するが、サブプロセスのサンドボックスにはシンボリックリンク混乱やWorkerネストによる脱出手法が報告されている。結局のところ、信頼境界として機能するサンドボックスは、言語ランタイムより下層──V8 Isolate、プロセス、カーネル──で実装する必要がある。

タイムラインと対策の優先順位

2026年1〜2月のサンドボックス脆弱性の公開タイムラインを整理する。

  • 1月26日:CVE-2026-22709(vm2、CVSS 9.8)がGitHub Advisory Databaseに公開
  • 1月27日:SandboxJSの最初のアドバイザリ(GHSA-wxhw-j4hc-fmq6)公開。同日、n8nのCVE-2026-1470(CVSS 9.9)開示
  • 1月31日:SandboxJSのプロトタイプ汚染アドバイザリ(GHSA-9p4w-fq8m-2hp7)公開
  • 2月2日:CVE-2026-25142(SandboxJS Ghost Accessor、CVSS 10.0)公開。n8nのCVE-2026-25049開示
  • 2月3日:SandboxJS v0.8.27緊急パッチリリース
  • 2月5〜6日:4件のCVSS 10.0脆弱性(CVE-2026-25520/25586/25587/25641)がNVDに同時公開
  • 2月8日:CVE-2026-25881(配列中間体攻撃)のアドバイザリ公開
  • 2月11日:CISAの脆弱性サマリ(SB26-040)にSandboxJS・vm2脆弱性が収録

対策の優先順位は以下の通りである。SandboxJSを使用している場合、v0.8.31への即座のアップデートが必須である。vm2は非推奨であるため、isolated-vmなど別のアプローチへの移行を強く推奨する。n8nはv2.5.2以降への更新が必要であり、即時更新が不可能な場合はワークフロー編集権限を信頼されたユーザに限定し、インスタンスをインターネットに直接公開しないことが求められる。

より長期的には、Node.jsエコシステムにおけるサンドボックスの設計思想そのものを見直す必要がある。ユーザランドのProxyベースサンドボックスを信頼境界として扱うことは構造的に破綻しており、V8 Isolate、プロセス隔離、WASM等のハードウェア支援型の隔離メカニズムへの移行が不可避である。

FAQ

SandboxJSのCVSS 10.0脆弱性はどのバージョンで修正されたのか?

主要4件(CVE-2026-25520/25586/25587/25641)はv0.8.29で修正された。CVE-2026-25142はv0.8.27、CVE-2026-25881はv0.8.31で修正されており、包括的な対策にはv0.8.31以上が必要である。

vm2は非推奨なのになぜまだ脆弱性が報告されるのか?

2023年7月の非推奨宣言後もnpmで週100万以上ダウンロードされており、多くのプロジェクトが依存を解消できていない。セキュリティ研究者は広く利用されているライブラリを継続的に調査するため、新たな脆弱性が発見・報告される。

プロトタイプ汚染によるサンドボックス脱出とは何か?

JavaScriptではオブジェクトがプロトタイプチェーンを通じてプロパティを継承する。攻撃者がサンドボックス内からホスト側のObject.prototype等を改変できると、サンドボックス外のコードにも影響が波及し、任意コード実行の足がかりとなる。

Node.jsで信頼できないコードを安全に実行する方法は?

isolated-vm(V8 Isolate利用)、プロセス隔離(コンテナ、VM)、WebAssemblyサンドボックスが代替となる。vmモジュールやそのラッパーライブラリを信頼境界として使用すべきではない。

n8nユーザが今すぐ取るべき対策は?

n8n v2.5.2以降への即時アップデートが最優先である。更新が困難な場合は、ワークフロー編集権限の厳格な制限と、インスタンスのインターネット非公開化で暫定的にリスクを低減できる。

参考文献