- Authors
- Name
- はじめに
- コンテナイメージの脅威モデル
- Trivy脆弱性スキャン
- Cosign/Sigstoreイメージ署名
- SBOM生成と管理
- CI/CDパイプライン統合
- Kubernetes Admissionポリシー
- 障害事例と復旧
- SLSAフレームワークとビルド来歴証明
- 運用チェックリスト
- まとめ
- 参考資料

はじめに
2024年のXZ Utilsバックドア事件(CVE-2024-3094)は、ソフトウェアサプライチェーン攻撃がいかに巧妙で危険であるかを世界中に示しました。 コンテナベースのデプロイが標準となった現在、1つのイメージに数百もの依存関係が含まれており、そのうち1つでも改ざんされれば本番環境全体が危険にさらされます。
この記事では、コンテナイメージセキュリティのライフサイクル全体を解説します。 脆弱性スキャン(Trivy)、イメージ署名(Cosign/Sigstore)、ソフトウェア構成表(SBOM)、そしてSLSAフレームワークを活用したサプライチェーンの完全性検証まで、 本番環境ですぐに適用できる実践的なコードとともに説明します。
コンテナイメージの脅威モデル
コンテナイメージを取り巻く攻撃ベクトルは、大きく5つに分類できます。
| 脅威タイプ | 説明 | 対策 |
|---|---|---|
| 既知の脆弱性(CVE) | ベースイメージやパッケージに含まれる公開脆弱性 | Trivy、Grypeスキャン |
| イメージ改ざん(Tampering) | レジストリ内でイメージが置換または変更される | Cosign署名 + 検証 |
| 依存関係の混乱(Dependency Confusion) | 悪意のあるパッケージが内部パッケージ名で登録される | SBOMベースの依存関係追跡 |
| ビルド環境の汚染 | CI/CDパイプライン自体が侵害される | SLSAビルド来歴証明 |
| 権限昇格(Privilege Escalation) | コンテナ内部からホストへの脱出 | 最小権限の原則 + ランタイムセキュリティ |
各脅威に対して単一のツールでは不十分であり、多層防御(Defense in Depth) 戦略が必要です。
Trivy脆弱性スキャン
Trivyの紹介とインストール
Trivyは、Aqua Securityが開発したオープンソースセキュリティスキャナーで、コンテナイメージ、ファイルシステム、Gitリポジトリ、Kubernetesクラスターをスキャンできます。 2026年現在、v0.68以上のバージョンでは読み取り専用データベースモードをサポートし、複数のプロセスが同時にスキャンを実行できます。
# Trivyインストール(macOS)
brew install trivy
# Trivyインストール(Linux)
curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sh -s -- -b /usr/local/bin
# バージョン確認
trivy version
イメージスキャンの実行
# 基本的なイメージスキャン - すべての重大度レベルを表示
trivy image nginx:1.25
# HIGHとCRITICALのみフィルタリング、修正可能な脆弱性のみ表示
trivy image --severity HIGH,CRITICAL --ignore-unfixed nginx:1.25
# JSON形式で結果を出力(CI/CDパイプライン連携用)
trivy image --format json --output results.json nginx:1.25
# 特定のCVEをスキップするための.trivyignoreファイルを使用
trivy image --ignorefile .trivyignore myapp:latest
# SBOMベースのスキャン(既存のSBOMを入力として使用)
trivy sbom ./sbom.cyclonedx.json
.trivyignoreファイルの構成
本番環境では、誤検知(false positive)やすぐに修正できない脆弱性を管理するために.trivyignoreファイルを活用します。
# .trivyignore - 承認された例外リスト
# 有効期限と理由を必ず記録すること
# CVE-2024-1234: 該当機能未使用、2026-04-01まで猶予
CVE-2024-1234
# CVE-2024-5678: アップストリームパッチ待ち
CVE-2024-5678
脆弱性スキャナーの比較
| 機能 | Trivy | Grype | Snyk | Clair |
|---|---|---|---|---|
| ライセンス | Apache 2.0 | Apache 2.0 | 商用(無料プランあり) | Apache 2.0 |
| コンテナイメージスキャン | O | O | O | O |
| ファイルシステムスキャン | O | O | O | X |
| IaCスキャン | O | X | O | X |
| SBOM生成 | O | X(Syft連携) | O | X |
| シークレット検出 | O | X | O | X |
| Kubernetesスキャン | O | X | O | X |
| CI/CD統合の容易さ | 高 | 高 | 非常に高い | 中 |
| オフラインサポート | O | O | X | O |
Trivyは単一ツールで最も広い範囲をカバーし、オフライン環境でも動作することが強みです。
Cosign/Sigstoreイメージ署名
Sigstoreアーキテクチャ
Sigstoreはソフトウェア署名のためのオープンソースプロジェクトで、3つの主要コンポーネントで構成されています。
- Cosign:コンテナイメージとOCIアーティファクトの署名・検証ツール
- Fulcio:OIDCベースの短期証明書発行CA(認証局)
- Rekor:署名記録を保存する不変(Immutable)な透明性ログ
キーレス(Keyless)署名方式では、開発者は別途鍵を管理する必要がなく、GitHub/GoogleなどのOIDC資格情報で署名できます。
Cosignを使ったイメージ署名
# Cosignインストール
brew install cosign
# 1. キーペア生成(従来の方式)
cosign generate-key-pair
# 2. イメージ署名(キーベース)
cosign sign --key cosign.key myregistry.io/myapp:v1.0.0
# 3. キーレス署名(Sigstore Fulcio使用 - 推奨)
# OIDC認証後に自動的に短期証明書を発行
cosign sign myregistry.io/myapp:v1.0.0
# 4. 署名検証
cosign verify --key cosign.pub myregistry.io/myapp:v1.0.0
# 5. キーレス署名検証(証明書発行者とIDを指定)
cosign verify \
--certificate-oidc-issuer https://token.actions.githubusercontent.com \
--certificate-identity-regexp "https://github.com/myorg/myrepo" \
myregistry.io/myapp:v1.0.0
署名へのメタデータ付与
Cosignは署名時にカスタムアノテーションを追加でき、ビルド来歴の追跡に有用です。
# ビルドメタデータを署名に含める
cosign sign \
-a "git.sha=$(git rev-parse HEAD)" \
-a "build.timestamp=$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
-a "build.pipeline=github-actions" \
myregistry.io/myapp:v1.0.0
# 署名とアノテーションの検証
cosign verify --key cosign.pub myregistry.io/myapp:v1.0.0 | jq .
SBOM生成と管理
SBOMとは
SBOM(Software Bill of Materials)は、ソフトウェアに含まれるすべてのコンポーネント、ライブラリ、依存関係のリストを構造化した文書です。 米国大統領令14028号(EO 14028)以降、連邦政府に納入するソフトウェアにはSBOMの提出が義務化されており、グローバルな規制の流れもSBOMを標準として求めています。
SBOMフォーマット比較
| 項目 | SPDX | CycloneDX |
|---|---|---|
| 主管機関 | Linux Foundation | OWASP |
| ISO標準 | ISO/IEC 5962:2021 | ECMA-424 |
| 主な用途 | ライセンスコンプライアンス | セキュリティ脆弱性管理 |
| サポート形式 | JSON, RDF, YAML, Tag-Value | JSON, XML, Protocol Buffers |
| VEXサポート | O | O |
| サービス依存関係の表現 | 限定的 | O |
| ツールエコシステム | 広い | 急速に成長中 |
ライセンスコンプライアンスが重要であればSPDX、セキュリティ脆弱性管理が優先であればCycloneDXを選択してください。 両方のフォーマットを生成するのが最も理想的です。
Syftを使ったSBOM生成
# Syftインストール
curl -sSfL https://raw.githubusercontent.com/anchore/syft/main/install.sh | sh -s -- -b /usr/local/bin
# CycloneDXフォーマットでSBOM生成(コンテナイメージ)
syft myregistry.io/myapp:v1.0.0 -o cyclonedx-json=sbom-cyclonedx.json
# SPDXフォーマットでSBOM生成
syft myregistry.io/myapp:v1.0.0 -o spdx-json=sbom-spdx.json
# ローカルディレクトリからSBOM生成
syft dir:./my-project -o cyclonedx-json=sbom.json
# TrivyでもSBOM生成が可能
trivy image --format cyclonedx --output sbom-trivy.json myregistry.io/myapp:v1.0.0
# SBOMをOCIレジストリに添付(Cosign活用)
cosign attach sbom --sbom sbom-cyclonedx.json myregistry.io/myapp:v1.0.0
SBOMドリフト検出
SBOMはビルド時点のスナップショットであるため、ランタイムにインストールされたパッケージと異なる場合があります。これをSBOMドリフトと呼びます。
# ランタイムSBOMとビルド時点SBOMの比較
# 1. ビルド時点SBOM(すでに生成済み)
# sbom-build.json
# 2. 実行中のコンテナから現在の状態を抽出
docker exec running-container syft / -o cyclonedx-json > sbom-runtime.json
# 3. 差分を比較
diff <(jq -r '.components[].name' sbom-build.json | sort) \
<(jq -r '.components[].name' sbom-runtime.json | sort)
CI/CDパイプライン統合
GitHub Actions統合パイプライン
以下は、ビルド、スキャン、署名、SBOM生成を1つのパイプラインに統合した例です。
name: Container Security Pipeline
on:
push:
branches: [main]
pull_request:
branches: [main]
env:
REGISTRY: ghcr.io
IMAGE_NAME: my-org/my-app
jobs:
build-scan-sign:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
id-token: write # キーレス署名に必要
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log in to GHCR
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and Push
id: build
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: ghcr.io/my-org/my-app:${{ github.sha }}
# Step 1: 脆弱性スキャン
- name: Trivy Vulnerability Scan
uses: aquasecurity/trivy-action@master
with:
image-ref: ghcr.io/my-org/my-app:${{ github.sha }}
format: sarif
output: trivy-results.sarif
severity: HIGH,CRITICAL
exit-code: '1'
- name: Upload Trivy Results to GitHub Security
uses: github/codeql-action/upload-sarif@v3
if: always()
with:
sarif_file: trivy-results.sarif
# Step 2: SBOM生成
- name: Generate SBOM with Syft
uses: anchore/sbom-action@v0
with:
image: ghcr.io/my-org/my-app:${{ github.sha }}
format: cyclonedx-json
output-file: sbom.cyclonedx.json
# Step 3: Cosignでイメージ署名(キーレス)
- name: Install Cosign
uses: sigstore/cosign-installer@v3
- name: Sign Container Image
run: |
cosign sign --yes \
-a "git.sha=${{ github.sha }}" \
-a "build.pipeline=github-actions" \
ghcr.io/my-org/my-app@${{ steps.build.outputs.digest }}
# Step 4: SBOM添付
- name: Attach SBOM to Image
run: |
cosign attach sbom \
--sbom sbom.cyclonedx.json \
ghcr.io/my-org/my-app@${{ steps.build.outputs.digest }}
# Step 5: SBOMアテステーション署名
- name: Sign SBOM Attestation
run: |
cosign attest --yes \
--predicate sbom.cyclonedx.json \
--type cyclonedx \
ghcr.io/my-org/my-app@${{ steps.build.outputs.digest }}
このパイプラインは以下の順序で動作します。
- DockerイメージをビルドしてGHCRにプッシュ
- TrivyでHIGH/CRITICALの脆弱性をスキャンし、検出時にパイプラインを停止
- SyftでSBOMを生成
- Cosignキーレス署名でイメージに署名
- SBOMをイメージに添付して署名
Kubernetes Admissionポリシー
Kyvernoによるイメージ署名検証
Kubernetesクラスターで署名されていないイメージのデプロイをブロックするには、Admission Controllerを活用します。 Kyvernoはポリシーベースのkubernetesネイティブツールで、イメージ署名検証を宣言的に定義できます。
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: verify-image-signature
spec:
validationFailureAction: Enforce
background: false
rules:
- name: verify-cosign-signature
match:
any:
- resources:
kinds:
- Pod
verifyImages:
- imageReferences:
- 'ghcr.io/my-org/*'
attestors:
- entries:
- keyless:
issuer: 'https://token.actions.githubusercontent.com'
subject: 'https://github.com/my-org/my-repo/.github/workflows/*'
rekor:
url: 'https://rekor.sigstore.dev'
attestations:
- type: 'https://cyclonedx.org/bom'
conditions:
- all:
- key: 'components'
operator: NotEquals
value: ''
OPA Gatekeeperによるポリシー
OPA Gatekeeperを使用する環境では、ConstraintTemplateを定義します。
apiVersion: templates.gatekeeper.sh/v1
kind: ConstraintTemplate
metadata:
name: k8srequireimagedigest
spec:
crd:
spec:
names:
kind: K8sRequireImageDigest
targets:
- target: admission.k8s.gatekeeper.sh
rego: |
package k8srequireimagedigest
violation[{"msg": msg}] {
container := input.review.object.spec.containers[_]
not contains(container.image, "@sha256:")
msg := sprintf(
"Container '%v' must use image digest (sha256) instead of tag: %v",
[container.name, container.image]
)
}
violation[{"msg": msg}] {
container := input.review.object.spec.initContainers[_]
not contains(container.image, "@sha256:")
msg := sprintf(
"Init container '%v' must use image digest (sha256) instead of tag: %v",
[container.name, container.image]
)
}
---
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sRequireImageDigest
metadata:
name: require-image-digest
spec:
match:
kinds:
- apiGroups: ['']
kinds: ['Pod']
namespaces:
- production
障害事例と復旧
事例1:署名なしイメージが本番環境にデプロイされた
状況:Admission Controllerが設定されていないNamespaceに、署名のないイメージがデプロイされました。
原因:Namespace単位のポリシー例外(exemption)が広すぎる範囲で設定されていました。
復旧手順:
- 該当Podを直ちに隔離(NetworkPolicyで外部通信を遮断)
- イメージをTrivyで緊急スキャン
- 同じイメージをCosignで事後署名するか、署名済みイメージに置き換え
- Admission Controllerポリシーの例外範囲を縮小
# 緊急隔離:NetworkPolicyを適用
kubectl apply -f - <<EOF
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: emergency-isolate
namespace: production
spec:
podSelector:
matchLabels:
app: compromised-app
policyTypes:
- Ingress
- Egress
EOF
# 緊急スキャン
trivy image --severity CRITICAL compromised-image:tag
事例2:CRITICALなCVEが本番環境で発見された
状況:すでに稼働中のサービスのベースイメージにCRITICAL脆弱性が公表されました。
復旧手順:
- 影響範囲の分析:SBOMを活用して該当パッケージを使用するすべてのイメージを特定
- 緊急パッチイメージのビルドとデプロイ
- ロールアウトの進行状況を監視
# SBOMから影響を受けるイメージを検索
for sbom in sbom-*.json; do
if jq -e '.components[] | select(.name == "libexpat")' "$sbom" > /dev/null 2>&1; then
echo "AFFECTED: $sbom"
fi
done
# パッチ済みベースイメージで再ビルド
docker build --no-cache --build-arg BASE_IMAGE=nginx:1.25.4-alpine -t myapp:patched .
# ローリングアップデート
kubectl set image deployment/myapp myapp=myregistry.io/myapp:patched
kubectl rollout status deployment/myapp
事例3:SBOMドリフトが発生した
状況:ランタイムコンテナに、ビルド時点のSBOMに存在しないパッケージがインストールされていました。
原因:コンテナ内部でapt-get installを実行する初期化スクリプトが存在していました。
対応:
- 不変(Immutable)コンテナの原則を強制するためreadOnlyRootFilesystemを設定
- ランタイムSBOM比較を定期的に実行するCronJobをデプロイ
- ドリフト発生時の自動アラートを構成
SLSAフレームワークとビルド来歴証明
SLSA(Supply-chain Levels for Software Artifacts)は、ソフトウェアサプライチェーンの完全性を保証するためのフレームワークです。 レベル0から3まで段階的にセキュリティ水準を高めることができます。
| レベル | 要件 | 説明 |
|---|---|---|
| SLSA 0 | なし | セキュリティ保証なし |
| SLSA 1 | ビルド来歴(Provenance)が存在 | ビルドプロセスが文書化されている |
| SLSA 2 | ホスティングされたビルドサービスの使用 | ビルドサービスが署名された来歴を生成 |
| SLSA 3 | 強化されたビルド環境 | 改ざん防止が保証されたビルド環境の使用 |
GitHub ActionsでSLSA Level 3のビルド来歴証明を実装するには、slsa-framework/slsa-github-generatorを活用します。
# SLSA Provenance生成ワークフロー
name: SLSA Build Provenance
on:
push:
tags:
- 'v*'
jobs:
build:
runs-on: ubuntu-latest
outputs:
digest: ${{ steps.build.outputs.digest }}
steps:
- uses: actions/checkout@v4
- name: Build Image
id: build
run: |
docker build -t ghcr.io/my-org/my-app:${{ github.ref_name }} .
DIGEST=$(docker inspect --format='{{index .RepoDigests 0}}' ghcr.io/my-org/my-app:${{ github.ref_name }} | cut -d@ -f2)
echo "digest=$DIGEST" >> "$GITHUB_OUTPUT"
provenance:
needs: build
permissions:
actions: read
id-token: write
packages: write
uses: slsa-framework/slsa-github-generator/.github/workflows/generator_container_slsa3.yml@v2.1.0
with:
image: ghcr.io/my-org/my-app
digest: ${{ needs.build.outputs.digest }}
secrets:
registry-username: ${{ github.actor }}
registry-password: ${{ secrets.GITHUB_TOKEN }}
運用チェックリスト
本番環境でコンテナイメージセキュリティを運用する際に、以下のチェックリストを活用してください。
ビルドフェーズ
- すべてのイメージが最小ベースイメージ(distroless、alpine)を使用しているか
- Dockerfileで固定バージョン(タグ + digest)を使用しているか
- マルチステージビルドでビルドツールが最終イメージに含まれていないか
- シークレットがイメージレイヤーに含まれていないか
スキャンフェーズ
- CIパイプラインでTrivyスキャンが必須で実行されているか
- CRITICAL脆弱性検出時にパイプラインが停止するか
- スキャン結果が一元化されたダッシュボードに収集されているか
- .trivyignoreの各項目に有効期限と理由が記録されているか
署名フェーズ
- すべての本番イメージにCosign署名が適用されているか
- キーレス署名を使用して鍵管理の負担を排除しているか
- Rekor透明性ログに署名が記録されているか
SBOMフェーズ
- すべてのイメージにSBOMが生成・添付されているか
- SBOMが署名されて改ざん防止が保証されているか
- SBOMドリフト検出が定期的に実行されているか
デプロイフェーズ
- Kubernetes Admission Controllerが署名検証を強制しているか
- イメージタグの代わりにdigestを使用しているか
- Namespace単位のポリシー例外が最小化されているか
まとめ
コンテナイメージセキュリティは、単一ツールではなく多層化された戦略で取り組む必要があります。 Trivyで既知の脆弱性を検出し、Cosign/Sigstoreでイメージの完全性を保証し、SBOMで構成要素を追跡し、SLSAでビルド来歴を証明することが、現代のソフトウェアサプライチェーンセキュリティの標準です。
最も重要なのは、これらすべてのプロセスを自動化することです。 CI/CDパイプラインにセキュリティゲートを統合し、Kubernetes Admission Controllerでポリシーを強制し、SBOMドリフトを継続的に監視してください。 セキュリティは一度の設定ではなく、継続的なプロセスです。