[クリティカル警告] Velocity.js v2.1.6にリモートコード実行 (RCE) の脆弱性 (GHSA-7gfh-x38p-prh3)
はじめに:なぜフロントエンドエンジニアも知るべきか
日本のフロントエンドエンジニアの皆さん、こんにちは。今回は、Node.js環境で利用されるテンプレートエンジン「velocityjs」に発見された、極めて危険な脆弱性「GHSA-7gfh-x38p-prh3」について解説します。この脆弱性は深刻度「Critical」のリモートコード実行(RCE)を引き起こすものであり、BFF(Backend For Frontend)やサーバーサイドレンダリング(SSR)など、Node.jsでサーバーサイドの処理を担うアプリケーションを開発している方にとっては見過ごせない情報です。
本脆弱性は、攻撃者が制御するVelocityテンプレートがアプリケーションによってレンダリングされた場合、サーバー上で任意のOSコマンドを実行可能にするものです。以前に修正されたプロトタイプ汚染の脆弱性(GHSA-j658-c2gf-x6pq)のパッチが不完全であったため、今回のRCEが発生しています。
脆弱性の詳細:不完全な修正とバイパス
この脆弱性の核心は、velocityjs v2.1.6における「プロパティ読み取り式」のフィルタリングの欠如にあります。以前のGHSA-j658-c2gf-x6pqでは、`#set`ディレクティブで値を割り当てる際の「ターゲットパス」(例: `$obj.__proto__.prop`)に対して、`__proto__`や`constructor`などの危険なキーをブロックする修正が施されました。
しかし、この修正はあくまで「代入先」のパスのチェックに留まり、「代入される値の評価」には適用されていませんでした。つまり、`#set($f = $expression)`のような式において、`$expression`が評価される過程でのプロパティアクセスには何の制限もなかったのです。
具体的には、`dist/cjs/compile/references.cjs`内の`getAttributes()`関数がプロパティアクセスを処理する際、`baseRef[property.id]`という形でプロパティにアクセスしますが、ここで`__proto__`や`constructor`といった危険なキーに対するフィルタリングが行われていませんでした。
RCEへの攻撃経路:Functionコンストラクタの悪用
このフィルタリングの抜け穴を悪用することで、攻撃者はJavaScriptの`Function`コンストラクタを間接的に呼び出し、任意のコードを実行する経路を構築しました。その手順は以下の通りです。
1. **`$x.constructor`**: まず、任意のオブジェクト`$x`の`constructor`プロパティにアクセスします。これにより、JavaScriptの`Object`コンストラクタを取得します。
2. **`.constructor`**: 次に、取得した`Object`コンストラクタの`constructor`プロパティにアクセスします。これにより、JavaScriptの`Function`コンストラクタを取得します。
3. **`(...)`**: 最後に、取得した`Function`コンストラクタに任意の文字列(例えば、`"return process.mainModule.require('child_process').execSync('whoami')"`のようなNode.jsのコード)を引数として渡して呼び出します。これにより、その文字列をボディとする関数が生成され、直ちに実行されます。
`#set`ディレクティブはこの結果をテンプレート変数に格納し、その変数を呼び出すことで最終的に任意のOSコマンドが実行されます。
PoC (Proof of Concept) の解説
以下は、この脆弱性を実証するPoCコードです。Velocityテンプレート内で`child_process`モジュールを利用し、`whoami`コマンドを実行しています。
```javascript\nconst velocity = require('velocityjs');\n\nconst template = "#set($f=$x.constructor.constructor(\"return process.mainModule.require('child_process').execSync('whoami').toString()\"))#set($r=$f())$r";\n\nconsole.log(velocity.render(template, { x: {} }));\n// Output: <current OS username>\n```
このコードを実行すると、`velocity.render`の内部で`child_process.execSync('whoami')`が実行され、現在のOSのユーザー名が出力されます。これは、サーバー上で任意のコマンドが実行できる状態にあることを明確に示しています。
影響とリスク:サーバー乗っ取りの可能性
この脆弱性の影響は非常に深刻です。RCE(Remote Code Execution)という分類が示す通り、攻撃者は脆弱なアプリケーションを実行しているサーバー上で、OSの任意のコマンドを実行できることになります。これにより、以下のような深刻な被害が発生する可能性があります。
特に、ユーザーからの入力をVelocityテンプレートに直接渡してレンダリングしているようなアプリケーションは、この脆弱性の直接的なターゲットとなります。フロントエンドの視点から見ると、ユーザー入力のサニタイズや、使用するライブラリのセキュリティアップデートは、サーバーサイドの安全性を担保するためにも非常に重要です。
対策:速やかなバージョンアップを
この脆弱性は、すでに以下のバージョンで修正されています。
`velocityjs`を利用しているプロジェクトでは、**直ちにバージョンを2.1.7以降にアップグレード**してください。npmを使用している場合は、以下のコマンドでアップデートできます。
```bash\nnpm update velocityjs\n```
または、`package.json`の依存関係を更新し、`npm install`または`yarn install`を実行してください。
また、今後も利用するライブラリのセキュリティ情報は常にチェックし、最新の状態を保つことが、モダンなウェブアプリケーション開発における重要な責務となります。
まとめ
velocityjs v2.1.6におけるリモートコード実行の脆弱性「GHSA-7gfh-x38p-prh3」は、テンプレートエンジンを介したサーバーサイドのセキュリティリスクの典型例です。フロントエンドエンジニアの皆さんも、利用しているライブラリやフレームワークが、BFFやSSRといったNode.js環境でどのように動作し、どのようなセキュリティリスクを抱えているのかを理解しておくことが不可欠です。
今回の件を教訓に、常にセキュリティ情報のアンテナを張り、迅速な対応を心がけましょう。安全なアプリケーション開発のために、ライブラリのバージョン管理とセキュリティアップデートは怠らないようにしてください。