Aid-On
Aid-On
00ホーム 01私たちについて 02製品紹介 03技術紹介 04ニュース 05会社概要 無料相談はこちら
Module

CVE・GHSA・OSV — 脆弱性データベースは何が違うのか

依存の脆弱性照合を支える3つの仕組み

「このライブラリに既知の脆弱性があります」という警告は、どこから来ているのか。npm auditもGitHubのDependabotも、裏では脆弱性データベースを引いている。ただ、そのデータベースは一枚岩ではない。CVE・GHSA・OSVという三つの名前が出てくる。役割はそれぞれ違う。違いが分かると、警告の読み方も、照合を自前で組むときの設計も見通しがよくなる。

CVE — 脆弱性につける共通の背番号#

CVE(Common Vulnerabilities and Exposures)は、脆弱性ひとつひとつにCVE-2021-44228のような一意の番号を振る仕組みだ。1999年に始まり、現在はMITREが運営している。

解決したのは名前の問題だった。同じ脆弱性を、ベンダーごとに違う名前で呼んでいた。番号が共通なら、報告書とパッチと検知ツールが同じものを指していると確認できる。2021年12月、Log4ShellがCVE-2021-44228という一つの番号で世界中に同時に伝わったのは、この仕組みがあったからだ。

番号を振るのはMITREだけではない。CNA(CVE Numbering Authority)として認定された組織——主要ベンダーやオープンソース財団、GitHubなど——が、自分の管轄範囲について番号を発行できる。採番を分散させることで、報告から公開までの時間を短くしている。

CVEだけでは足りなかった#

ただ、CVEはソフトウェア全般を対象にした汎用の仕組みだ。パッケージエコシステムとの相性は良くない。理由は三つある。

影響範囲が散文で書かれている。 「Apache Log4j2 2.0-beta9から2.15.0まで」——人間ならこれで通じる。機械はそうはいかない。「いま入っている2.14.1は該当するか」を判定するには、この文を解釈しなければならない。しかも書き方に決まりがないので、表記の揺れも多い。

バージョンの比較規則がエコシステムごとに違う。 npmのsemver、PythonのPEP 440、Goの疑似バージョン、Mavenのバージョン順序。同じ「2.0以上3.0未満」を表すのにも、書き方と比べ方が別々になっている。汎用の形式ひとつで表そうとすると、どこかで無理が出る。

すべての脆弱性にCVEが振られるわけではない。 エコシステム内で報告され、パッチが出て、CVE番号を取らずに終わるものは珍しくない。CVEだけを見ていると、これらが丸ごと視界から消える。

GHSA — エコシステムに寄せたデータベース#

そこで各エコシステムが独自のデータベースを持ちはじめた。よく知られているのはGitHub Advisory DatabaseのGHSAで、GHSA-jfh8-c2jp-5v3qのような形式のIDを持つ。ほかにPythonのPyPA Advisory Database、RustのRustSec、GoのGo Vulnerability Databaseがある。

これらは対象パッケージ名とバージョン範囲を、そのエコシステムの規則で機械可読に持つ。CVEがあるものにはCVE番号を併記し、ないものにも独自IDを振る。実務で効くのは後者だ。CVE未採番の脆弱性も拾える。

一方で、データベースが分かれたことで別の問題が生まれた。形式もバラバラになったのだ。ツールを作る側は、npm用・Python用・Rust用にそれぞれパーサを書くことになる。同じ脆弱性が複数のデータベースに別IDで載っていて、重複を排除する必要も出てくる。

OSV — 形式を揃えて、まとめて引けるようにする#

OSV(Open Source Vulnerabilities)は、この形式を統一する試みだ。Googleが中心となってOSV Schemaという共通のJSON形式を定め、各エコシステムのデータベースをその形式に変換して osv.devに集約している。

要点はaffectedという構造にある。パッケージ名とエコシステムを明示したうえで、バージョン範囲を「導入されたバージョン(introduced)」と「修正されたバージョン(fixed)」の組で表す。散文ではない。突き合わせがそのまま実装できる。

aliasesフィールドで別IDとの対応も持つ。GHSAとCVEが同じ脆弱性を指していれば、そこで結びつく。重複の排除がデータ側で解決されている。

取り下げられた勧告のためのwithdrawnフィールドもある。誤りだった、あるいは重複だったと後から判明した勧告は、削除ではなく取り下げとして記録される。ツール側は、一度出した警告を撤回する判断ができる。

実際に照合すると何が起きるか#

手元のプロジェクトと照合する手順は、単純化すると三段階になる。

  1. いま何が入っているかを確定するpackage-lock.jsonpnpm-lock.yamlのようなロックファイルを読む
  2. エコシステムとパッケージ名で候補を絞る — 同名でもnpmとPyPIは別物なので、エコシステムの区別が要る
  3. バージョン範囲に入るかを判定する — ここでエコシステムごとの比較規則を使う

一段階目が要点だ。package.json^1.2.0という記述では、実際に入る版が決まらない。ロックファイルがないプロジェクトは照合できない。「版が固定されていない」こと自体が、脆弱性の有無以前の問題になる。

もう一つ効いてくるのが、間接依存(トランジティブ依存)の扱いだ。直接インストールした覚えのないパッケージが、依存の依存として入っている。実際の警告の多くはここから出る。ロックファイルは依存ツリー全体を記録しているので、この層まで照合できる。

ツールから見ると、形式の統一は効き方が大きい。npm向け・Python向けと別々に書いていた照合の処理が、一本にまとまる。対応するエコシステムを増やすときも、足すのはバージョン比較の規則だけでよくなる。

「該当する」と「危ない」は別#

照合で「該当」と出ても、それがそのまま危険を意味するとは限らない。脆弱性のある関数を一度も呼んでいない、という状況は普通にある。到達可能性(reachability)まで見ないと、実際に影響があるかは分からない。

CVSSスコアも同じだ。あれは脆弱性そのものの深刻度であって、そのアプリケーションでの深刻度ではない。外部に公開していないバッチ処理と、認証前のエンドポイント。同じスコアでも意味がまるで違う。

「該当するが影響しない」を機械可読に記録するためのVEX(Vulnerability Exploitability eXchange)のような取り組みもある。ただし判断そのものは人が下す。データベースが答えるのは「入っているか」までだ。「危ないか」は文脈の問題になる。

通信せずに照合する#

osv.devはAPIを公開している。ただし照合のたびに問い合わせると、どのパッケージを使っているかが外部に伝わる。依存構成は、それ自体が知られたくない情報であることも多い。社内システムの構成が、脆弱性チェックのついでに漏れていく。

OSVはデータベース全体のダンプも配布している。手元に落として定期的に更新すれば、照合は完全にローカルで完結する。日次や月次の鮮度で足りる用途なら、この形が扱いやすい。オフラインの検査環境でも動く。

まとめ#

CVEは共通の背番号、GHSAなどは各エコシステムの実務データベース、OSVはそれらを共通形式に揃えて集約する層——という重なりになっている。どれかが他を置き換えるわけではない。役割が違う。

そして照合の精度は、データベースの質だけでは決まらない。ロックファイルで版が固定されているかというプロジェクト側の状態。該当した脆弱性が実際に到達可能かという文脈の判断。この二つが、警告を実務で使えるものにするかどうかを分ける。

技術紹介一覧に戻る

AIと、もっと先へ。

まずはお気軽にご相談ください。目的や課題を丁寧にヒアリングし、 ご予算や納期に合わせた最適なご提案をいたします。

無料相談はこちら