C/C++ ライブラリ構成識別#
概要#
脆弱性を継続的に管理するには、利用中のソフトウェアとバージョンを正確に把握する構成管理が欠かせません。 しかし、C/C++ プロジェクトではパッケージマネージャーを使わずに依存ライブラリのソースコードを直接プロジェクトへ取り込むケースが多くあります。 標準的な手法だけでは、構成管理や脆弱性特定が困難でした。
こうした状況を踏まえ、FutureVuls では C/C++ ライブラリ構成識別機能を提供しています。 本機能は、取り込んだ C/C++ ライブラリのソースコードからハッシュ値を生成し、FutureVuls 上でリポジトリやバージョンを自動同定・管理できるようにします。 具体的には、製品によく同梱される zlib、libxml2、expat などの主要ライブラリに幅広く対応しています。
【コラム】C/C++ ライブラリの構成管理が複雑になる背景
システムの脆弱性を管理する上で、どのライブラリのどのバージョンを使用しているかを把握する「構成管理」は欠かせません。 しかし、C/C++プロジェクトにおいては、この構成管理が一筋縄ではいかないことが多々あります。 なぜC/C++では構成管理が複雑化しやすいのか、ここではその背景を整理します。
統一的なパッケージマネージャーの不在#
C/C++ では、他言語に比べてパッケージマネージャーが広く普及していません。 通常、言語標準のパッケージマネージャーがあれば lockfile をそのまま構成管理に活用できますが、C/C++ ではその利用は限定的です。 実際、C/C++ エコシステムを対象とした研究(ASE 2022発表の Tang らの調査1)によると、GitHub 上の C/C++ リポジトリ 24,001 件のうち、Conan や vcpkg などのパッケージマネージャーを利用しているリポジトリは、わずか 1.8% にとどまりました。
主流となる 2 つのライブラリ取り込み方式#
パッケージマネージャーが普及していないため、実際のプロジェクトでは以下の 2 つの方式を組み合わせて利用するのが一般的です。
- OS パッケージを利用する方式:
aptなどの OS パッケージマネージャーでライブラリをシステム領域へインストールし、ビルド時に CMake などがそこから探索してリンクします。 - ソースコードを直接取り込む方式: ライブラリのソースコードをプロジェクト内に直接配置・ビルドします。
ソース直接取り込みにおける識別の難しさ#
ここで、これら2つの方式について、脆弱性スキャンのための構成管理という視点から見てみます。
まず、OS パッケージを利用する方式であれば、OS のパッケージマネージャーからソフトウェアの一覧、台帳となる情報を取得可能です。 FutureVuls のローカルスキャンも、この OS パッケージ情報を取り込む仕組みになっています。
一方、ソースコードを直接取り込む方式では、ソフトウェア台帳を自動生成できません。 さらに、脆弱性管理へ繋げるためには、製品名やバージョンを CPEなどの識別子として正確に同定・紐付ける必要がありますが、ソースコードの直接配置ではそうした識別情報が得られません。
こうした背景を踏まえ、FutureVuls では、このライブラリソースを構成管理と脆弱性管理へスムーズに取り込むために、「C/C++ ライブラリ構成識別」機能を提供しています。
対応環境#
バイナリは静的リンク済みで、追加の依存関係はありません。
| OS / architecture |
|---|
linux/amd64 |
linux/arm64 |
windows/amd64 |
同定対象#
本機能で解析・同定できるファイル形式、対応ライブラリ、および送信データの仕様は以下のとおりです。
対象ファイル#
指定したディレクトリ内に含まれる、以下の拡張子を持つファイルが対象です。
.c,.cc,.cpp,.h,.hh,.hpp(小文字の拡張子のみ。.Cや.Hは対象外です)
サブディレクトリも対象です。ただし、名前が . で始まるディレクトリ(.git など)とシンボリックリンクは対象外です。
改行コードが CRLF のファイルは同定できません
ファイルの内容は、改行コードを変換せずにそのままハッシュ化します。
照合先のハッシュは改行コードが LF のソースから作られているため、CRLF に変換されたファイルは一致しません。
Windows で Git の core.autocrlf を有効にして取得したソースなど、改行コードが CRLF のファイルを含む場合は、futurevuls-cpp-fingerprint が警告を表示します。
改行コードが LF のままのソースを指定してください。
対応ライブラリ#
現在は Google/OSS-Fuzz で管理されている C/C++ オープンソースプロジェクトを対象としています(プロジェクト一覧)。 FutureVuls が、受け取ったハッシュ値を外部サービスの OSV(Open Source Vulnerabilities)に問い合わせることで、リポジトリとバージョンを同定します。
送信されるデータ#
- FutureVuls に送信されるデータ: 指定したディレクトリの末尾名(コンポーネント名として利用)、および各ファイルの MD5 ハッシュ値
- 送信されないデータ: ソースコードの本文、ファイル名、絶対・相対パス情報
FutureVuls から OSV へは、ハッシュ値のみを問い合わせます。
利用手順例#
本機能は、主に以下の流れで利用します。
- 構成識別データの生成:専用コマンド
futurevuls-cpp-fingerprintを実行し、ソースコードの構成情報(ハッシュ値)を記録したfuturevuls-cpp-library.jsonを生成する - FutureVuls への送信:生成された JSON ファイルをローカルまたは CI/CD パイプラインなどから FutureVuls API へ送信する
- 自動同定:FutureVuls がハッシュ値を照合してライブラリのリポジトリとバージョンを特定する
- 同定結果の確認:同定結果をコンソール画面または FutureVuls API から確認する
送信は、初回は Lockファイル追加 API(POST /v1/lockfile)、登録済みの futurevuls-cpp-library.json を更新する場合は Lockファイル更新 API(PUT /v1/lockfile/{lockfileID})を使用します。
lockfileID は「lockfileIDの取得方法」で確認できます。
以下の利用例では、初回の送信(POST)を紹介します。
事前準備#
API 送信には、API 権限に「更新」を含むグループトークンを発行してください。
このトークンを FVULS_TOKEN に指定します。
SERVER_ID:serverIDの取得方法FVULS_TOKEN:FutureVuls APIのトークン管理FVULS_SERVER_UUID:UUIDの取得方法(CI/CD 環境への組み込み の場合のみ)
以下の例では、取得スクリプトのダウンロードや構成識別データの生成に失敗した場合は処理を中止し、古い JSON ファイルを送信しないようにしています。
Linux#
set -euo pipefail
# 取得スクリプトで、指定したディレクトリのソースをハッシュ化し、futurevuls-cpp-library.json を生成する
curl -fsSL -o futurevuls-cpp-fingerprint.sh https://installer.vuls.biz/futurevuls-cpp-fingerprint.sh
bash futurevuls-cpp-fingerprint.sh <library-directories> # 同定対象のライブラリのディレクトリを 1 つ以上指定する
# 例: bash futurevuls-cpp-fingerprint.sh third_party/*/
# 例: bash futurevuls-cpp-fingerprint.sh third_party/zlib third_party/libxml2
# 生成したファイルを Base64 化してリクエストボディを作り、POST
# (ファイルが大きいとコマンドの引数に収まらないため、ボディはファイルに書き出して送る)
SERVER_ID=<server-id> # 事前準備した SERVER_ID を指定する
FVULS_TOKEN=<api-token> # 事前準備した FVULS_TOKEN を指定する
{
printf '{"serverID":%s,"path":"futurevuls-cpp-library.json","fileContent":"' "${SERVER_ID}"
base64 -w 0 futurevuls-cpp-library.json
printf '"}'
} > futurevuls-cpp-request.json
curl -fsS -X POST 'https://rest.vuls.biz/v1/lockfile' \
-H 'Content-Type: application/json' \
-H "Authorization: ${FVULS_TOKEN}" \
--data-binary @futurevuls-cpp-request.json
Windows#
$ErrorActionPreference = "Stop"
# 取得スクリプトを保存
curl.exe -fsS -o futurevuls-cpp-fingerprint.bat https://installer.vuls.biz/futurevuls-cpp-fingerprint.bat
if ($LASTEXITCODE -ne 0) { throw "futurevuls-cpp-fingerprint download failed: $LASTEXITCODE" }
# futurevuls-cpp-library.json を生成
cmd /c ".\futurevuls-cpp-fingerprint.bat <library-directories>" # 同定対象のライブラリのディレクトリを 1 つ以上指定する
# 例: cmd /c ".\futurevuls-cpp-fingerprint.bat third_party\zlib third_party\libxml2"
# 例: third_party 配下の各ディレクトリを指定する場合(パスに空白を含まないこと)
# $dirs = (Get-ChildItem -Directory third_party).FullName
# cmd /c ".\futurevuls-cpp-fingerprint.bat $dirs"
if ($LASTEXITCODE -ne 0) { throw "futurevuls-cpp-fingerprint failed: $LASTEXITCODE" }
# 生成したファイルを Base64 化して POST
$fileContent = [Convert]::ToBase64String([IO.File]::ReadAllBytes((Resolve-Path .\futurevuls-cpp-library.json).ProviderPath))
$serverID = <server-id> # 事前準備した SERVER_ID を指定する
$fvulsToken = "<api-token>" # 事前準備した FVULS_TOKEN を指定する
$body = @{ serverID = $serverID; path = "futurevuls-cpp-library.json"; fileContent = $fileContent } | ConvertTo-Json -Compress
Invoke-RestMethod -Method Post -Uri "https://rest.vuls.biz/v1/lockfile" -Headers @{ Authorization = $fvulsToken } -ContentType "application/json" -Body $body
プロキシ環境の場合
取得スクリプトとバイナリのダウンロードには、プロキシの設定が必要です。
Linux は「プロキシ設定」を参考に環境変数 https_proxy を設定してください。
Windows は環境変数 VULS_PROXY にプロキシサーバの URL を設定し、curl.exe には --proxy を指定してください。
CI/CD 環境への組み込み#
GitHub Actions では、構成識別データを生成した後に futurevuls/fvuls-lockfile-uploader でアップロードします。
path には futurevuls-cpp-library.json を指定してください。
name: Upload C/C++ library identification data
on:
push:
branches:
- <branch-name> # リリース対象のブランチ名を指定する
paths:
- "<library-directory>/**" # 同定対象のライブラリ配置ディレクトリを指定する
permissions:
contents: read
jobs:
upload-cpp-library:
runs-on: ubuntu-26.04
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Generate C/C++ library identification data
shell: bash
run: |
set -euo pipefail
curl -fsSL -o futurevuls-cpp-fingerprint.sh https://installer.vuls.biz/futurevuls-cpp-fingerprint.sh
bash futurevuls-cpp-fingerprint.sh <library-directories> # 同定対象のライブラリのディレクトリを 1 つ以上指定する
# 例: bash futurevuls-cpp-fingerprint.sh third_party/*/
# 例: bash futurevuls-cpp-fingerprint.sh third_party/zlib third_party/libxml2
- name: Upload C/C++ library identification data
uses: futurevuls/fvuls-lockfile-uploader@9245e71ad5a5833acf0196650027fc4a6be78921 # v2
env:
# GitHub Repository Secret を設定する
FVULS_SERVER_UUID: ${{ secrets.FVULS_SERVER_UUID }}
FVULS_TOKEN: ${{ secrets.FVULS_TOKEN }}
with:
repoName: ${{ github.repository }}
path: "./futurevuls-cpp-library.json"
結果の確認#
同定結果は、アプリケーション詳細ページ および FutureVuls API GET /v1/lockfile/{lockfileID} から確認できます。
構成識別できない場合についても同ページ、API からエラーメッセージを確認できます。
制限事項#
ファイルの上限#
1 ファイルには最大 50 コンポーネントを含められます。
1 コンポーネントには最大 5,000 個のハッシュを含められます。
上限を超えたファイルは、FutureVuls がファイル全体を拒否します。
なお、futurevuls-cpp-fingerprint コマンドは上限超過時に警告を表示しますが、JSON 自体は出力します。
同定結果と再試行#
FutureVuls はコンポーネント単位で構成識別を行います。 一致するリポジトリがない場合、候補リポジトリが複数残る場合、またはバージョンを特定できない場合は、該当するコンポーネントを登録しません。
また、OSV への照会が 1 件でも失敗した場合、同定できた他のコンポーネントも含めて登録しません。 ネットワークエラー等で OSV に接続できなかった場合は、再アップロードにより再試行が可能です。
CPE が採番されていないライブラリの扱い#
本機能によってリポジトリおよびバージョンが正しく同定された場合であっても、対象のソフトウェアに対して NVD などで公式の CPE が採番されていない場合は、FutureVuls 上で CPE としての登録および CPE に基づく脆弱性の紐付けは行われません。
-
出典: Wei Tang et al., "Towards Understanding Third-party Library Dependency in C/C++ Ecosystem", ASE 2022. https://doi.org/10.1145/3551349.3560432 ↩