コンテンツにスキップ

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 へは、ハッシュ値のみを問い合わせます。

利用手順例#

本機能は、主に以下の流れで利用します。

  1. 構成識別データの生成:専用コマンド futurevuls-cpp-fingerprint を実行し、ソースコードの構成情報(ハッシュ値)を記録した futurevuls-cpp-library.json を生成する
  2. FutureVuls への送信:生成された JSON ファイルをローカルまたは CI/CD パイプラインなどから FutureVuls API へ送信する
  3. 自動同定:FutureVuls がハッシュ値を照合してライブラリのリポジトリとバージョンを特定する
  4. 同定結果の確認:同定結果をコンソール画面または FutureVuls API から確認する

送信は、初回は Lockファイル追加 API(POST /v1/lockfile)、登録済みの futurevuls-cpp-library.json を更新する場合は Lockファイル更新 API(PUT /v1/lockfile/{lockfileID})を使用します。 lockfileID は「lockfileIDの取得方法」で確認できます。 以下の利用例では、初回の送信(POST)を紹介します。

事前準備#

API 送信には、API 権限に「更新」を含むグループトークンを発行してください。 このトークンを FVULS_TOKEN に指定します。

以下の例では、取得スクリプトのダウンロードや構成識別データの生成に失敗した場合は処理を中止し、古い 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 に基づく脆弱性の紐付けは行われません。


  1. 出典: Wei Tang et al., "Towards Understanding Third-party Library Dependency in C/C++ Ecosystem", ASE 2022. https://doi.org/10.1145/3551349.3560432 ↩