** 本記事は、JavaScript bugs aplenty in Node.js ecosystem – found automatically の翻訳です。最新の情報は英語記事をご覧ください。**
先日開催された 2022 年 USENIX カンファレンスで、「Mining Node.js Vulnerabilities via Object Dependence Graph and Query (オブジェクト依存グラフおよびクエリを用いた Node.js の脆弱性マイニング)」というタイトルの興味深い論文が発表されました。
この論文は ODGEN (Object Dependence Graph Generator: オブジェクト依存グラフジェネレーター) と呼ばれる、ソースコードの静的分析手法について述べています。本記事では、論文の核心となる部分については詳述しません (読む際には、多少の数学と、オペレーショナルセマンティクス記法の知識があることが望ましいです)。
その代わりに、ODGEN ツールを実際に使った自動検索で、論文の筆者らが Node Package Manager (NPM) の JavaScript エコシステム上に何を発見したかに焦点を当てます。
ここで一つ重要なことは、上述の通り、論文の筆者らが使用したツールはいわゆる静的分析を目的としたものだということです。
静的分析とは、ソースコードを実際に実行することなく、コーディングの誤りやセキュリティホールの可能性 (あるいは存在) を確認する分析手法を指します。
プログラムを逐一実行してテストを行うのは、設定および実行に多くの時間を要するため、非効率的です。
しかし、ご想像の通り、実際にソフトウェアを構築し、制御された状態を保ちつつ実データで動作させる、いわゆる動的分析は、「注意深くコードを確認して、実際の動作を直感的に判断する」よりも、はるかに詳細な結果を得られ、難解で危険なバグを高確率で発見します。
しかし、動的分析は時間を要する上に、有効に活用できるとは限りません。
動的分析は、たとえ何年もの歳月を費やしたとしても、簡単に無駄になり得ます。なぜなら、大多数のテストがそれほど顕著な違いを示すことなく、どのソフトウェアにも特に問題が発見されない場合が多いからです。ソフトウェアの動的分析は、例えるなら毎年同じ試験問題を出題する教師のようなものです。「過去問」の演習ばかりしていた生徒が、純粋にその科目を習得した生徒と同等の成績を出してしまいます。
複雑に絡まり合うサプライチェーンの依存関係<
NPM、PyPI、PHP Packagist、RubyGems などのグローバルなオープンソースリポジトリのように、最近の巨大なソフトウェアソースコードエコシステムでは、多くのソフトウェア製品がサードパーティ製の大規模なソフトウェアリポジトリに依存しており、サプライチェーンにおける依存関係を不規則かつ複雑にしています。
ご想像の通り、サプライチェーンには、それぞれの基本パッケージが提供する動的分析ツールへの依存関係も存在します。さらに、これらのテストツールは通常、すべてのパッケージを組み合わせて独自のアプリケーションを形成する時にどのような相互作用が発生するかを考慮していません (考慮できません)。
したがって、静的分析はそれ単体では分析手法として不十分ですが、ソフトウェアリポジトリをスキャンして目立ったセキュリティホールを発見するための出発点としては非常に優れています。また、「オフライン」で行えるのも大きな利点の 1 つです。
プログラムを実際に構築する必要も、現実のさまざまな場面で実行することを想定したそれらしいテストスクリプトを考え出さなくとも、使用するすべてのソースコードパッケージを定期的かつ日常的にスキャンできます。
さらに、使う必要のないパッケージも含めてソフトウェアリポジトリ全体をスキャンし、実際にソフトウェアをテストする前に信用できないコード (または作者) を選別できます。
その上、動的分析で (あるいはバグ報奨金制度で) あるソフトウェアにプログラミング上の失敗によるバグが発見された場合に、同種のバグが他にも存在しないかどうか、静的分析を用いてすべてのソフトウェアを調べられることがあります。
たとえば、あるコードの特定の箇所で、use-after-free メモリエラーを誘発するコーディング上の誤りが発見され、バグとして報告されたとしましょう。
use-after-free エラーとは、メモリブロックを使用し終え、他の場所で使用できるように解放した後、解放したことを忘れてそのまま使い続けてしまうことで生じる不具合を指します。例えるなら、引っ越しをしてから数か月後、無意識のうちにかつての住所に車で帰宅して、車庫に見慣れない車が止まっているのを不思議に思うようなものです。
もし、誰かがその脆弱性を含んだコードをコピーして、企業のリポジトリ内の他のソフトウェアコンポーネントにペーストしていたとしても、コードの全体的な構造が保たれており、コメントや変数名がそれほど変更されていなければ、文字列検索で発見できる可能性があります。
しかし、もし他のプログラマーが純粋に元のコードに沿って、バグを含んだコードを別のプログラミング言語に (専門用語で「異なる字句で」) 書き換えていたとしたら、文字列検索はほとんど意味をなさなくなります。
静的分析の可能性
そこで、文字列ではなく、コードフローやデータの依存関係などの機能的な特徴に基づいて、コードベース全体を静的に検索して、プログラミング上の失敗を発見できたら便利ではないでしょうか。
上述した USENIX の論文は、多くの異なるコードの特徴を組み合わせ、「コードが入力をどのように出力に変換するか、そしてコードのどの部分が結果に影響を与えるか」を示す簡潔な表現に変換する、静的分析ツールの構築を試みています。
このツールの処理は、上述のオブジェクト依存関係グラフに基づいて行われます。
ごく簡単に説明すると、ソースコードに静的なラベルを付けることで、ある時点で使用するコードとデータの組み合わせ (オブジェクト) が、後から使用するオブジェクトに影響を与えるかどうかを確認できる、というものです。
この仕組みを活用することで、実際にソフトウェアを動かしてテストしたり、ソースコードの文字列検索だけに頼ったりせずとも、既知の悪意のあるコードの動作 (専門用語で「匂い」) を検索できるようになるはずです。
言い換えると、あるコードが欠陥を含んだ元コードからそのままコピーされたものであっても、元コードの形式だけ踏襲したものであっても、元コードと同じく職場の誤った慣習にしたがって作成されたものであっても関係なく、元コードから発見されたバグを新コードからも検出できる可能性があるということです。
簡単に言うと、優れた静的分析は、実際に動作するソフトウェアを確認しなくても、広範囲で厳密な実環境テストを行っても現れないほど微妙な (あるいは稀な) プログラミングの失敗を、プロジェクトに採用される前に未然に特定するのに役立ちます。
これこそが、本記事でお伝えしたかったことです。
30 万個のパッケージを処理
この論文の著者らは、NPM リポジトリ内の 30 万個の JavaScript パッケージに ODGEN システムを適用し、脆弱性を含む可能性があると同システムが判断したパッケージをフィルタリングしました。
フィルタリングしたパッケージのうち、1 週間のダウンロード数が 1,000 件以上のパッケージに対して (すべての結果を処理する時間がなかったようです) 調査を進め、悪用可能なバグが存在すると思われるパッケージを特定しました。
結果として、筆者らは 80 件のコマンドインジェクションの脆弱性 (信頼できないデータをシステムコマンドに渡すことで、リモートコード実行など望ましくない結果をもたらす脆弱性)、14 件のさらなるコード実行のバグを含む、180 件の有害なセキュリティバグを発見しました。
このうち 27 件は、最終的に CVE 番号を付与され、「正式な」セキュリティホールとして認められました。
残念ながら、この論文は 2 年以上前に行われた実用部分の検証をまとめて書かれたものなので、これらの CVE はすべて 2019 年および 2020 年のものです。
とはいえ、学術界ほど純粋に研究に取り組む余裕のない環境 (多くのサイバーセキュリティ対応者にとって、最近のサイバー犯罪者との戦いは、研究を急いで終わらせ、即座に実用に移すことを意味します) で働いていたとしても、最近の超大規模ソフトウェアリポジトリにおけるサプライチェーン攻撃に対抗するための研究テーマとして、静的コード分析は非常に重要です。
動的言語に対する静的分析
JavaScript など、人気のある動的言語に対する静的処理は難解なため、ここ数年間、静的分析は少し敬遠されているようです。
たとえば、JavaScript の変数は、ある時点で整数だったとしても、その後テキスト文字列が正当に「追加」されることでテキスト文字列に変化したり、全く別のオブジェクト型に変化したりする可能性があります
また、動的に生成されたテキスト文字列も、まったく新しい JavaScript プログラムに変化し、コンパイルされて実行されることがあるため、静的分析が行われたときには存在しなかった動作 (およびバグ) が出現する可能性があります。
しかし、上述の論文は、動的言語においても、依存するリポジトリの定期的な静的分析が非常に有効であることを示唆しています。
静的分析ツールは、たとえ JavaScript であっても、すでに使用しているコードに潜むバグを発見できるだけでなく、採用を検討しているパッケージのコードの根本的な品質を判断するのに役立ちます。
サプライチェーン攻撃の防止について詳しく知る
以下のポッドキャストでは、ソフォスの専門家である Chester Wisniewski (ソフォスのプリンシパルリサーチサイエンティスト) が、Kaseya や SolarWinds といった過去の大規模攻撃から得られる教訓に基づき、サプライチェーン攻撃への対処について有益かつ実行可能なアドバイスを多数紹介しています。
オーディオプレーヤーが表示されない場合は、Soundcloud (英語) で直接聴くこともできます。
また、ポッドキャストの全文をトランスクリプト (英語) としてお読みいただくこともできます。