GoのSCA何使おうか悩んでいる人へ

サムネイル

はじめに

こんにちはこんばんは!プラットフォーム開発本部認可チームの日比です。

プラットフォーム開発本部では、DMMの各プロダクトが共通で利用できるマイクロサービス群を提供しています。
認可チームは、マイクロサービスの認可システムを開発・運用しています。

本記事では、GoにおけるSCA1ツールの選定について、Nancyとgovulncheckを比較した結果を共有します。
運用方法などについては取り上げないため、あらかじめご了承ください。

背景

認可チームでは、SCAツールにNancyを採用していました。
しかし、Nancyが利用しているOSS Indexのポリシー変更に伴い、GitHub Actionsからの未認証リクエストの実行は拒否されるようになりました。その結果、ワークフローの停止という問題が発生しました。
Nancyの認証対応を行って継続利用するか、Go公式ツール等の他ツールへ移行するかを判断するため、移行コストおよびセキュリティの観点から比較検証を実施しました。

Nancyとは?

Nancyは、Sonatype社によって開発されたGo向けのOSS脆弱性スキャンツールです。
このツールは、エンジニアの作成したアプリケーションに依存する外部ライブラリに、既知のセキュリティホールが含まれていないか確認します。

具体的には、Goの依存関係管理ファイルである go.sum に記録されたパッケージ情報を読み取り、Sonatype社の管理する脆弱性データベースOSS Indexと照合します。
これにより、エンジニアは自身の書いたコードだけでなく、サプライチェーンに潜むリスクを可視化することが可能となります。

Nancyのメリット

Nancyを採用する最大の利点は、そのバックエンドにあるOSS Indexの信頼性にあります。
このデータベースは、公的なCVE2情報だけでなく、セキュリティ専門企業としての独自の調査結果も含まれており、情報の網羅性と更新頻度において高い品質を誇っています。

Nancyのアプローチは、「脆弱性のあるバージョンのライブラリは、使用の有無にかかわらずプロジェクトに含めるべきではない」という厳格なセキュリティポリシーと親和性が高いです。
コンプライアンス要件の厳しい金融・医療などのドメインでは、該当コードを実行していなくとも、脆弱性を含むアーティファクトの存在自体をリスクと見なす場合があります。
そのような要件下では、バージョンベースで厳しく判定するNancyの特性が強みとなります。

Nancyのデメリット

一方で、Nancyはバージョンに依存した判定ロジックゆえの構造的な課題を抱えています。
アプリケーションがライブラリ内の安全な機能のみを利用している場合であっても、そのライブラリの別機能に脆弱性が存在すれば、Nancyは警告を発します。
これはFalse Positiveと呼ばれ、エンジニアに対して不要な調査や対応を強いる原因となり、セキュリティ警告に対する感度を鈍らせるリスクがあります。

govulncheckとは?

govulncheckは、Goチームによって公式に提供されているツールであり、コードおよび依存パッケージに含まれる既知の脆弱性を検出するために設計されています。

従来、GoのセキュリティチェックはNancyやSnykといったサードパーティ製のツールに依存していましたが、Go 1.18以降のセキュリティ強化の一環として、エコシステム自体に統合された標準的なソリューションとして登場しました。

このツールは、単なるバージョンの照合にとどまらず、Goの特性を深く理解した解析をする点が特徴です。
公式が運用するGo Vulnerability Databaseを情報源とし、エンジニアが安心してコードを記述・運用できる環境をネイティブに提供することを目的としています。

govulncheckのメリット

govulncheckを採用する最大の利点は、コールグラフ解析に基づく圧倒的な検知精度の高さにあります。
従来のバージョン照合型のツールでは、依存ライブラリに脆弱性が含まれているだけで一律に警告が発せられていましたが、govulncheckは脆弱な関数が実際にコードから呼び出されているかを検証します。

これにより、実質的なセキュリティリスクを伴わないFalse Positive3が劇的に排除されます。
エンジニアは、アプリケーションに真の脅威をもたらす脆弱性のみに集中して対応できるため、トリアージ4にかかる調査コストを大幅に削減することが可能となります。

修正作業における透明性の高さも特筆すべき点です。
警告時には、ユーザーのコードから脆弱なライブラリの関数に至るまでの具体的なスタックトレースが提示されます。
これにより、なぜ警告が出ているのか、またコードのどこを修正すれば呼び出しを回避できるかを即座に理解できます。

=== Symbol Results ===

Vulnerability #1: GO-202X-XXXX
    Panic when validating certificates with DSA public keys in crypto/x509
  More info: https://pkg.go.dev/vuln/GO-2025-4013
  Standard library
    Found in: crypto/x509@go1.25
    Fixed in: crypto/x509@go1.25.2
    Example traces found:
      #1: for function crypto/x509.Certificate.Verify
        COMMAND_OR_PACKAGE_NAME @ FILE_PATH:y:x

govulncheckのデメリット

一方で、govulncheckのReachability5を重視するアプローチは、特定の文脈においてはデメリットとなり得ます。
最大のリスクは、プロジェクト内に脆弱性を含むバージョンのライブラリが残留し続けることです。

govulncheckが現在の段階で呼び出されていないため安全と判断した場合、エンジニアはライブラリのアップグレードを見送る可能性があります。
しかし、将来的なコードの変更によってその脆弱な関数が呼び出されるようになった瞬間、検知されていなかった脆弱性が顕在化し、リスクに晒されることになります。

つまり、潜在的な時限爆弾を抱えたまま開発を続けることになりかねません。

加えて、バイナリスキャンにおける制約も存在します。
govulncheckはビルド情報やシンボルテーブルに依存して解析するため、cgo部分の脆弱性検知や情報が削除されたバイナリ、あるいは難読化されたコードに対しては、検知能力の低下または機能しない可能性がある点にも留意が必要です。

検証

検証項目

本検証の目的は、Nancyとgovulncheckの挙動差異を定量的かつ定性的に比較し、セキュリティポリシーに合致するツールを選定することです。

以下の統一基準を用いて判定しました。

項目名 期待される挙動 判定基準
真正脆弱性の検知 コード内で使用している脆弱な関数を危険として検知できるか False Negativeがないことの確認
潜在脆弱性の扱い go.modにはあるが使用していない脆弱なライブラリへの反応 False Positive頻度を確認
情報の解像度 チームメンバーが修正するために必要な情報(ファイル名、行数、修正バージョン)が具体的か 修正時の工数への影響
実行速度 GitHub Actionsにおける処理時間 ボトルネックにならないか

検証環境

  • 対象言語: Go 1.23
  • 実行ツール: act

脆弱性の選定根拠

本検証では、ツールの特性であるReachabilityの解析能力と網羅性を対照的に評価するため、以下の2つの基準に基づいて検証対象を選定しました。

1. 実行パスに含まれる真正な脅威(True Positiveのベンチマーク)

対象:golang.org/x/crypto
選定理由:
Goの準標準ライブラリであり、認可システムにおいて極めて重要度の高いパッケージです。
今回は、コード内で脆弱性のある関数を明示的に呼び出すことで、現実に悪用可能なリスクを構成しました。
これにより、ツールが重大な脆弱性を確実に見つけられるかFalse Negative6の不在を検証します。

2. 実行パスに含まれない潜在的な脆弱性(False Positiveのベンチマーク)

対象:gopkg.in/yaml.v2
選定理由:
多くのプロジェクトで利用される一般的なライブラリです。
今回は、go.mod には依存として存在するものの、コード内では一切利用しない、あるいは脆弱な関数を通らない状態を構成しました。
これにより、実害のないリスクに対してツールが過剰に反応せず、適切に除外できるかを検証します。

検証準備

意図的に脆弱性のある古いバージョンのライブラリを追加しました。

modulesの変更

require (
    golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2
    gopkg.in/yaml.v2 v2.2.2
)

codeの変更

import (
    "crypto/rand"  // 脆弱性のある関数を実際に使用(rand.Int呼び出し)
    yaml "gopkg.in/yaml.v2"  // 脆弱性のあるライブラリをimportするが使用しない
)

// 未使用の変数でyamlパッケージの存在を保持(コンパイラの最適化を回避)
var _ = yaml.Unmarshal

GitHub Actions ワークフローの作成

ログ解析を行い、項目を抽出するスクリプトを実行しました。

検証結果

検証において、同一のソースコードに対しNancyおよびgovulncheckを実行したところ、その検知能力と信頼性において決定的な差異が確認されました。

検証項目 観点 Nancy govulncheck
検知された脆弱性の数 検知数 0 7
標準ライブラリ脆弱性 使用中のcryptoを検知できるか ❌️(未検知) ✅️(検知)
潜在脆弱性 未使用のyamlをどう扱うか ✅️(無視) ✅️(無視)
情報解像度 修正に必要な詳細情報 中 (CVE + CVSS) 高 (コールスタック7付き)
実行速度 処理にかかった時間 2570ms 8025ms

まず、真正脆弱性の検知能力についてです。
Nancyは、今回意図的に混入させた脆弱性を1件も検知できませんでした。
これには、実際にコード内で関数を呼び出しており、攻撃可能な状態にあるcryptoパッケージの脆弱性も含まれます。
セキュリティツールとして、本来検知すべきリスクを見落とすFalse Negativeが発生していると言わざるを得ません。

対してgovulncheckは、標準ライブラリを含めた合計7件の脆弱性を正確に検出しました。
特にcryptoパッケージに関しては、Call Graph解析によって実際に脆弱な関数が呼ばれていることを特定したうえで警告を出しており、検知精度の高さが実証されました。

次に、潜在脆弱性の排除についてです。
両ツールともに、未使用であるyaml.v2に対しては警告を出しませんでした。
govulncheckはコードから到達不可能であると解析したうえでまさしく除外判定しています。
一方、Nancyはバージョンベースで判定するツールであるにもかかわらず検知しませんでした。これは意図した除外ではなく、依存関係の解析漏れ(スキャン不全)の可能性が高いと考えられます。

パフォーマンスの観点ではNancyが高速でしたが、まずはリスクをしっかり捉えられることを大切にしています。
正確にリスクを洗い出し、かつ使われていない脆弱性を論理的に除外できるgovulncheckの方が、結果として運用上の信頼性は圧倒的に高いという結果となりました。

考察

今回の検証結果でもっとも重大な発見は、Nancyが crypto パッケージに含まれる致命的な脆弱性を1件も検知できなかったという事実です。

検証環境の go.sum には対象のライブラリとバージョンが正しく記載されていましたが、Nancyはこれを検知しませんでした。
Nancyは、主にセマンティックバージョニングされたリリース番号の照合に依存しています。
そのため、今回のような準標準ライブラリやコミットハッシュで指定されたバージョンに対しては、データベースとの照合がうまくいかず、スキャン対象外、あるいは安全と誤判定された可能性が高いと考えられます。

認可というサービスの性質上、cryptonet/httpといったコア機能に含まれる脆弱性は、システム全体のセキュリティ崩壊に直結します。これらを検知できない状態での運用は、極めてハイリスクです。

Nancyはgo.sumに基づくスキャンを行い、今回の検証環境のように標準ライブラリや特定の依存関係に対してスキャンは機能しないケースが確認されました。
対してgovulncheckは、Go言語公式のツールとして標準ライブラリまで包括的にカバーしており、何が危険で、何が安全かを正確に判断できています。

現状の課題である「ワークフロー停止」を解消するだけであれば、Nancyを継続利用する方が短期的には低コストです。
Sonatype社のアカウント(無料)を作成し、認証情報をCIに追加するだけで復旧できるためです。

しかし、検証で明らかになったFalse Negativeのリスクを抱えたまま運用を続けることは、推奨できません。

一方、govulncheckへの移行には、GitHub Actionsのワークフローファイルの書き換えという初期コストが発生します。
また、検証結果が示すとおり、実行時間は約4倍に増加しますが、GitHub Actions全体における数秒の遅延は許容範囲内であり、開発サイクルへの悪影響は軽微です。

むしろ、TCO8の観点ではgovulncheckに優位性があると考えられます。
govulncheckは実際にコードから呼び出されている脆弱性のみを報告するため、エンジニアによる「この警告は本当に影響があるのか?」という調査のトリアージコストを大幅に削減できます。
Nancyのように検知が不安定、あるいはバージョンだけで一律判定する場合、調査コストが増大するか、最悪の場合リスクが見過ごされることになります。

実行時間は約4倍(数秒程度)増加しますが、CI/CD全体への影響は軽微であり、得られるセキュリティ強度の向上と比較すれば許容範囲内です。

まとめ

本記事では、GoにおけるSCAツールとして、Nancyとgovulncheckの比較検証を行いました。

検証の結果、以下の点が明らかになりました。

  • 信頼性: govulncheckは標準ライブラリや主要な依存先も網羅し、False Negativeのリスクが低い
  • 検知能力: govulncheckは、従来見逃されていた脆弱性を7件を正確に検出
  • 精度: govulncheckは実際に使用されている脆弱性のみを報告し、ノイズが少ない
  • 実行速度: Nancyの方が高速。※検知精度を重視するため、速度の優先度は低いと判断

認可のような重要なシステムにおいては、検知精度の高さが最優先事項です。
動かないワークフローを直すという目先の対応にとどまらず、将来的なセキュリティリスクを確実に低減させるため、govulncheckへの移行を現在提案しております。


  1. Software Composition Analysis(ソフトウェア構成分析)
  2. Common Vulnerabilities and Exposures(共通脆弱性識別子)
  3. 偽陽性。問題がないのにあると判定されること(過検知)。
  4. 重要度や緊急度に応じて対応の優先順位を決定・選別すること
  5. 到達可能性
  6. 偽陰性。問題があるのにないと判定されること(見逃し)。
  7. プログラムの実行中に発生する関数呼び出しの順序を管理するメモリ領域
  8. Total Cost of Ownership(総所有コスト)