SBOMを出し、脆弱性データベースと突き合わせると、警告が大量に出ます。数百件になることも珍しくありません。そしてそのほとんどは、実際には影響しません。使っていない関数の脆弱性、到達できない経路の脆弱性、既に別の手当てで塞がっている脆弱性。これを一件ずつ人が調べ直す状態を終わらせるための形式が、VEX(Vulnerability Exploitability eXchange)です。
「該当する」と「危ない」の隙間
照合が出すのは、あくまで版の一致です。手元にlog4j-core 2.14.1が入っていて、その版にCVE-2021-44228が該当する。ここまでは機械が判定できます。
判定できないのはその先です。その脆弱性のあるコードを、自分は実際に呼んでいるのか。 呼んでいたとして、攻撃者が入力を制御できる経路にあるのか。
答えは製品ごとに違います。同じライブラリを積んでいても、使い方が違えば結論は変わります。この判断は、その製品を作った人にしかできません。
VEXは、その判断を機械可読な形で外に出す仕組みです。
四つの状態
VEXの中心は単純です。ある製品とある脆弱性の組に対して、状態を一つ付けます。
- not_affected:影響しない
- affected:影響する
- fixed:修正済み
- under_investigation:調査中
under_investigationがあるのが実務的なところです。公表直後は結論が出ていません。何も言わないより、調べていると言うほうが受け取る側は動きやすくなります。
そしてnot_affectedと言うときは、理由を添えます。ここがVEXの肝になります。
理由を分類する
「影響しません」だけでは、受け取った側が信じる根拠を持てません。VEXは理由を決められた区分から選ばせます。
- component_not_present:その部品自体が入っていない
- vulnerable_code_not_present:部品は入っているが、問題のコードは含まれない
- vulnerable_code_not_in_execute_path:含まれるが、実行経路に乗らない
- vulnerable_code_cannot_be_controlled_by_adversary:経路に乗るが、攻撃者が制御できない
- inline_mitigations_already_exist:既に別の手当てで塞いである
区分になっていることに意味があります。自由記述だと読み手が毎回解釈することになりますが、決まった値なら機械が処理できます。「影響しない」と主張する製品を、理由の種類ごとに絞り込めます。
inline_mitigations_already_existだけは注意が要ります。手当てが外れれば影響するようになるので、その手当て自体を管理下に置いておく必要があります。
三つの書き方
形式は一つに定まっていません。用途の違う三つが並立しています。
CSAFはOASISの標準で、そのなかにVEXのプロファイルがあります。もともとはベンダーがセキュリティ勧告を配るための形式で、VEXはその一部として位置づけられます。重厚なぶん、表現力があります。
OpenVEXはOpenSSFの仕様で、必要最小限に絞ってあります。一つの文書に一つの主張、という単純さを保っています。生成も解釈も軽く済みます。
CycloneDXはSBOMの形式そのものにVEXを持てます。部品表と影響有無を同じ文書に置けるので、扱いが一体になります。
どれを選ぶかは、誰に渡すかで決まります。既にCSAFで勧告を配っているならCSAF、SBOMと一緒に配るならCycloneDX、自動化の部品として軽く扱いたいならOpenVEXが向きます。
実際の中身
OpenVEXの文書は、驚くほど短いものです。一つの主張は、対象と脆弱性と状態と理由だけで書けます。
{
"@context": "https://openvex.dev/ns/v0.2.0",
"author": "Example Corp",
"timestamp": "2026-09-06T09:00:00Z",
"statements": [
{
"vulnerability": { "name": "CVE-2021-44228" },
"products": [{ "@id": "pkg:maven/com.example/app@1.4.0" }],
"status": "not_affected",
"justification": "vulnerable_code_not_in_execute_path"
}
]
}
製品の指定にpurlを使っているのが要点です。SBOMと同じ識別子なので、部品表とVEXが同じ名前で突き合わさります。ここが揃っていないと、人手で対応表を作る羽目になります。
timestampも効きます。いつ時点の判断かが分かるので、その後に状況が変わったかを追えます。
誰が書き、誰が読むか
VEXは、上流から下流へ流れます。
製品を作った側が書きます。ライブラリの作者、アプリケーションの開発会社、機器のベンダー。自分の製品について、その脆弱性が効くかどうかを宣言します。
受け取った側が読みます。照合で出た警告に対して、供給元のVEXがあれば「調べ済み」として扱えます。なければ自分で調べます。
この流れが成立すると、同じ調査が組織の数だけ繰り返されなくなります。 一社が一度判断すれば、下流の全員がその結果を使えます。VEXが解こうとしているのは、技術の問題ではなく、この重複の問題です。
警告が減るとは限りません
VEXを導入する動機は「警告を減らしたい」であることが多いものです。ただ、減り方には偏りがあります。
減るのは、供給元が整備している範囲だけです。 大手のベンダー製品やよく使われるOSSは、VEXが出てくる可能性があります。小さな依存や社内製のものは出てきません。
そして依存の数でいえば、後者のほうが圧倒的に多くなります。全体の警告数が劇的に減る、という期待は外れます。
効くのはむしろ質のほうです。手を付けるべきものが、手を付けなくていいものに埋もれなくなります。 数が減らなくても、順序が付けば動けます。
自社が供給する側なら、話は逆になります。VEXを出すことで、顧客からの問い合わせが減ります。「御社の製品にこの脆弱性は影響しますか」という質問に、毎回個別に答えなくてよくなります。
過信しないための注意
VEXは主張であって、証明ではありません。 書いたのは供給元で、その内容を第三者が検証しているわけではありません。誰の主張かを見て、信用の度合いを決める必要があります。
古くなります。 製品の版が変われば、実行経路も変わります。以前not_affectedだったものが、次の版では該当することがあります。VEXは製品の版に紐づくものとして扱ってください。
ないことは、安全を意味しません。 供給元がVEXを出していないのは、影響しないからではなく、単に出していないからです。空白をnot_affectedと読み替えてはいけません。
全件は出てきません。 現実には、VEXを整備しているのは一部の供給元に限られます。届かない分は、結局こちらで判断することになります。
状態の意味を取り違えないでください。 fixedは修正済みの版があるという意味であって、こちらが更新したという意味ではありません。更新して初めて、自分にとっての解決になります。
どこから手を付けるか
自社で扱うなら、受け取る側と出す側で始め方が違います。
受け取る側は、まず供給元がVEXを出しているかを確かめるところから始まります。出ていれば取り込み、出ていなければ従来どおり自分で判断します。取り込みに対応した照合ツールも増えてきました。
出す側は、全部を書こうとしないほうが続きます。現実的なのは、問い合わせが多い脆弱性から順に書くやり方です。同じ質問が三度来たなら、それは書くべき一件になります。
書式は最初から凝らなくて構いません。OpenVEXなら数行で始められるので、運用に乗せてから形式を検討しても遅くありません。続かない仕組みを作るより、粗くても回るほうが価値が出ます。
まとめ
SBOMが「何を持っているか」、脆弱性データベースが「何が壊れているか」、VEXが「それは自分に効くか」。三つで一組になります。
前の二つは機械が処理できますが、三つ目だけは人の判断が要ります。VEXがやったのは判断の自動化ではなく、人が下した判断を、機械が運べる形に変えたことのほうです。
判断そのものは減りません。同じ判断を何度も繰り返すことが減ります。警告の数が増え続ける以上、効いてくるのはこちらの効率になります。自社の運用を見直すなら、脆弱性の警告に対して「影響しないと判断した理由」がどこに残っているかを確かめてください。担当者の記憶や個別のメールにしか残っていなければ、同じ調査が次の公表時にもう一度発生します。