自分のDockerイメージの中に何が含まれているか、正確に把握していますか?
多くのエンジニアは、まずシンプルな目的からDockerを使い始めます。「アプリが動くようにDockerfileを書き、ビルドしたイメージをDocker HubやAWS ECRにプッシュする」。一見すると、FROM node:18-bullseye-slimから始まるDockerfileは非常に軽量に見えるかもしれません。しかし実際には、自身で明示的にインストールしていないにもかかわらず、90以上のDebianパッケージ(libssl、libc6、curlなど)やネストされた無数のnpmライブラリが引き込まれています。
2021年にLog4jの脆弱性(CVE-2021-44228)が発覚した際、テックリードやSecOpsチームからチャットで最初に飛んできた質問は、おそらく「本番環境にLog4jを含むイメージはあるか?」というものだったはずです。もしその時、各サーバーにSSH接続し、稼働中のコンテナに逐一execでログインして.jarファイルを検索していたとしたら、初動対応で何時間も遅れをとってしまったことになります。
基本要素:SBOM、Syft、Grype
SBOM(ソフトウェア部品表)とは?
SBOMは、食品パッケージに印刷されている「原材料表示」や、PCパーツの「構成パーツ一覧」のような役割を果たします。コンテナの世界におけるSBOMは、パッケージ名、正確なバージョン、作者、ライセンス、SHA-256ハッシュ値など、あらゆる構成要素を明確にリストアップします。この一覧には、OSレイヤーのパッケージ(apt、apk、rpmなど)とアプリケーションレイヤーのライブラリ(npm、pip、maven、Goモジュールなど)の両方が含まれます。
現在、業界標準として主に2つのフォーマットが普及しています。
- SPDX (Software Package Data Exchange): Linux Foundationによるオープン標準。ソースコードの著作権やライセンスのコンプライアンス確認に最適化されています。
- CycloneDX: OWASPコミュニティが主導する標準。セキュリティリスクの管理やサプライチェーン分析に特化して設計されています。
Anchore発の強力なペア:SyftとGrype
レイヤー解析スクリプトを自前で書く代わりに、DevSecOpsコミュニティではAnchoreが提供するオープンソースツールの組み合わせが広く支持されています。
- Syft: イメージ、ファイルシステム、コードリポジトリをスキャンし、わずか2〜4秒で標準的なSBOMファイルを抽出するCLIツール。
- Grype: Syftが出力したSBOMファイルを直接読み込む脆弱性スキャナー。継続的に更新されるCVE情報ソース(NVD、GitHub Advisories、Alpine SecDBなど)とデータを照合し、問題のあるパッケージを正確に特定します。
実践:SBOMの生成とスキャンの全手順
ステップ1: SyftとGrypeのインストール
LinuxやmacOSでは、公式のスクリプトを使って手軽にインストールできます。
# Syftのインストール
curl -sSfL https://raw.githubusercontent.com/anchore/syft/main/install.sh | sh -s -- -b /usr/local/bin
# Grypeのインストール
curl -sSfL https://raw.githubusercontent.com/anchore/grype/main/install.sh | sh -s -- -b /usr/local/bin
# バージョン確認
syft version
grype version
Homebrewが利用可能なmacOS環境であれば、brew install syft grypeを実行するだけで完了します。
ステップ2: サンプルDockerイメージの準備
動作検証のため、意図的に脆弱性のある古い依存関係を含んだNode.jsアプリを作成してみましょう。
mkdir sbom-demo && cd sbom-demo
npm init -y
npm install express [email protected]
続いてDockerfileを作成します。
FROM node:18-bullseye-slim
WORKDIR /app
COPY package*.json ./
RUN npm install --omit=dev
COPY . .
CMD ["node", "index.js"]
ローカル環境でイメージをビルドします。
docker build -t my-app:1.0.0 .
ステップ3: SyftによるSBOMの抽出
ターミナル上で含まれるパッケージを素早く確認したい場合は、イメージ名を指定するだけです。
syft my-app:1.0.0
パッケージ名、現在のバージョン、パッケージタイプ(deb、npmなど)が一覧で表示されます。次に、CycloneDX JSON形式に準拠した完全なSBOMファイルを抽出してみましょう。
syft my-app:1.0.0 -o cyclonedx-json > sbom-my-app.json
生成されるファイルは通常数千行におよび、ファイルサイズは300KB〜1MB程度になります。JSON構造には深い依存関係ツリーが含まれています。componentsやhashes、ライセンス情報などを確認・検索したい場合は、toolcraft.app/ja/tools/developer/json-formatterに貼り付けてフォーマット・折りたたみ表示すると見やすくなります。
プロジェクトの規約でSPDX形式が必要な場合は、フォーマットフラグをspdx-jsonに変更します。
syft my-app:1.0.0 -o spdx-json > sbom-my-app.spdx.json
ステップ4: Grypeによる脆弱性スキャン
SBOMファイルを保持しておく最大のメリットは、セキュリティチェックを実行するたびに数百MBもあるイメージを保持したりプルし直したりする必要がない点です。わずか数百KBのJSONファイルをGrypeに渡すだけでスキャンできます。
grype sbom:./sbom-my-app.json
コマンドの実行は2秒もかからず完了します。Grypeは詳細な解析レポートを出力します。
NAME INSTALLED FIXED-IN TYPE VULNERABILITY SEVERITY
lodash 4.17.20 4.17.21 npm CVE-2021-23337 High
lodash 4.17.20 4.17.21 npm CVE-2020-28500 Medium
結果から、[email protected]に深刻度Highの脆弱性(テンプレート処理によるコマンドインジェクション)が存在することが一目瞭然です。バージョンを4.17.21に引き上げるだけで問題を根本から解消できます。
ステップ5: GitHub Actionsで脆弱性を含むイメージをブロックする
デプロイ完了後に手動でスキャンする運用は避けるべきです。最善のアプローチは、CI/CDパイプライン内の必須の品質ゲート(Quality Gate)としてこのチェックを組み込むことです。
以下のワークフローファイルは、SBOMを自動生成し、深刻度High以上の脆弱性が検出された場合にパイプラインをフェイルさせます。
name: Docker Security Pipeline
on:
push:
branches: [ "main" ]
jobs:
build-and-scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build Docker Image
run: docker build -t my-app:${{ github.sha }} .
- name: Generate SBOM with Syft
uses: anchore/sbom-action@v0
with:
image: my-app:${{ github.sha }}
format: cyclonedx-json
output-file: sbom.json
- name: Scan Vulnerabilities with Grype
uses: anchore/scan-action@v4
with:
sbom: sbom.json
fail-build: true
severity-cutoff: high
開発者が深刻なCVEを含むライブラリを誤って追加してしまったとしても、CIのゲートでプルリクエストがブロックされ、本番環境への混入を未然に防ぐことができます。
おわりに
イメージのビルドプロセスにSyftとGrypeを導入することで、セキュリティ管理を受動的な対応から能動的なアプローチへと転換できます。すべてのコミットに対して、透明性の高い「証明書」としてSBOMファイルが付属するようになります。未知のゼロデイ脆弱性が発生した際も、サーバー群を慌てて調査する代わりに、手元のSBOMファイルを数秒クエリするだけで即座に影響範囲を特定できるようになります。
