コンテナイメージ脆弱性スキャン入門

Linux・CLI・DevOpsカテゴリを表すパンダのイラスト Linux・CLI・DevOps

本記事はGeminiの出力をプロンプト工学で整理した業務ドラフト(未検証)です。

コンテナイメージ脆弱性スキャン入門

実務家のセキュリティエンジニアとして、コンテナを活用する現代のシステム開発において、コンテナイメージの脆弱性スキャンは避けて通れないセキュリティ対策です。本記事では、その重要性と実践方法について解説します。

脅威モデル

コンテナイメージの脆弱性は、組織にとって多岐にわたる脅威をもたらします。主な脅威モデルは以下の通りです。

  1. 既知の脆弱性の悪用: ベースイメージ、ランタイムライブラリ、アプリケーションコードに含まれる既知の脆弱性(CVE)が攻撃者によって悪用され、リモートコード実行 (RCE)、情報漏洩、サービス妨害 (DoS) に繋がる可能性があります。

  2. サプライチェーン攻撃: 信頼できないソースからのイメージやコンポーネントがCI/CDパイプラインに混入し、悪意のあるバックドアやマルウェアが組み込まれるリスクがあります。

  3. 情報漏洩: コンテナイメージ内にハードコードされた認証情報、APIキー、秘密鍵などが含まれている場合、イメージが漏洩することで機密情報が露呈します。

  4. 権限昇格/横展開: 脆弱性を悪用した攻撃者は、コンテナ内部からホストOS、他のコンテナ、さらにはクラスタ全体への権限昇格や横展開を試みます。

攻撃シナリオ

以下に、コンテナイメージの脆弱性を悪用した典型的な攻撃シナリオをMermaidのAttack Chainで示します。

graph TD
    A["攻撃者"] -->|"脆弱なイメージの特定"| B("コンテナレジストリ/リポジトリ")
    B -->|"公開されているイメージのダウンロード"| C("脆弱性分析")
    C -->|"既知の脆弱性(CVE)を特定"| D{"攻撃コードの準備"}
    D -->|"コンテナ実行環境へのデプロイを待つ"| E["CI/CDパイプライン"]
    E -->|"脆弱なイメージがデプロイされる"| F["コンテナ実行環境(Kubernetes/Docker)"]
    F -->|"脆弱性を悪用したコマンド実行"| G("コンテナ内部の侵害")
    G -->|"権限昇格/横展開"| H["ホストOS/他のクラスタリソース"]
    H -->|"機密情報窃取/サービス妨害"| I("情報漏洩/システム停止")

シナリオ解説:

  1. 脆弱なイメージの特定: 攻撃者は公開されているコンテナレジストリ(Docker Hubなど)や企業のリポジトリを偵察し、古いバージョンのイメージや、既知の脆弱性を含む可能性のあるイメージを特定します。

  2. 脆弱性分析と攻撃コード準備: 特定したイメージをダウンロードし、その構成要素(OSパッケージ、ライブラリ)を分析して、利用可能なCVEを見つけ出します。その後、そのCVEを悪用するためのエクスプロイトコードを準備します。

  3. デプロイを待つ: 攻撃者は準備したエクスプロイトコードを実行する機会を待ちます。CI/CDパイプラインが脆弱なイメージを本番環境にデプロイすると、攻撃機会が生まれます。

  4. コンテナ内部の侵害: デプロイされた脆弱なコンテナに対し、外部から脆弱性を悪用したリクエストを送信。成功すると、攻撃者はコンテナ内で任意のコマンドを実行できるようになります。

  5. 権限昇格と横展開: 侵害されたコンテナのコンテキスト内で、追加の脆弱性(例:設定ミス、カーネルの脆弱性)を探し、コンテナからホストOSへの権限昇格を試みます。成功すれば、ホスト上で動作する他のコンテナやクラスタリソースへの横展開が可能となり、最終的に機密情報の窃取やシステム停止といった甚大な被害を引き起こします。

検出と緩和

検出

コンテナイメージの脆弱性スキャンは、ライフサイクルの様々な段階で実施し、多層防御を構築することが重要です。

  • 開発段階 (Pre-commit/Pre-push): 開発者がローカルでイメージビルド前に、簡易的なスキャンツールを用いて脆弱性をチェックします。初期段階での修正はコストが低いです。

  • CI/CDパイプライン (ビルド時): Dockerfileが変更された際や新しいイメージがビルドされる際に、自動的に脆弱性スキャンツール(Trivy, Clair, Anchore, Snykなど)を実行し、既定の閾値を超える脆弱性があればビルドを失敗させます。

  • コンテナレジストリ (プッシュ時/定期的): イメージがレジストリにプッシュされた際にスキャンを実行し、保存されている既存のイメージも定期的に再スキャンします。新たなCVEが公開された際に迅速に検知するためです。

  • ランタイム環境 (Admission Controller): Kubernetes環境などでは、脆弱性スキャン結果に基づき、Admission Controllerが脆弱なイメージのデプロイを阻止するように設定できます。

緩和

検出された脆弱性への対処だけでなく、予防的な緩和策も重要です。

  • 信頼できるベースイメージの使用: 公式または自社で厳選・パッチ適用された軽量なベースイメージ(例: scratch, alpine, distroless)を使用します。

  • 最小限のコンポーネント: Dockerfileで必要なパッケージとライブラリのみをインストールし、不要なツールやサービスは含めません(多段階ビルドの活用)。

  • SBOM (Software Bill of Materials) の生成と活用: イメージに含まれる全てのソフトウェアコンポーネントとそのバージョンを明確にし、脆弱性情報の追跡を容易にします。

  • イメージ署名と検証: 信頼できる署名が付与されたイメージのみがデプロイされるように強制します。これにより、改ざんされたイメージの実行を防ぎます。

  • 脆弱性パッチの迅速な適用: スキャンで検出された脆弱性に対し、速やかにパッチを適用したイメージを再ビルドし、デプロイします。

  • 最小権限原則: コンテナ内で実行されるプロセスは、必要最小限の権限で動作させます(例: USER命令でroot以外のユーザーを指定)。

運用対策

スキャンポリシーとCI/CD連携

実運用では、どのレベルの脆弱性を許容するか、明確なポリシーを定めます。例えば、CVSSv3スコアが7.0以上のCritical/High脆弱性が検出された場合は、ビルドを自動的に失敗させ、デプロイをブロックする運用が一般的です。

# 例: TrivyでCritical/High脆弱性があればビルドを失敗させる (CI/CDスクリプト内)


# Trivyはデフォルトでexit code 1を返すため、CI/CDツールで失敗と判断される


# --severity CRITICAL,HIGH はスキャン対象の脆弱性レベルを指定


# --ignore-unfixed は未修正の脆弱性を無視するオプション(注意して使用)

echo "--- イメージ脆弱性スキャン開始 ---"
docker pull myrepo/myapp:latest
trivy image --severity CRITICAL,HIGH myrepo/myapp:latest

if [ $? -ne 0 ]; then
  echo "--- Critical/High脆弱性が検出されました。ビルドを停止します。 ---"
  exit 1
else
  echo "--- 脆弱性スキャンをパスしました。 ---"
fi

鍵/秘匿情報の安全な取り扱い

コンテナイメージ内の秘匿情報(APIキー、認証情報、データベースパスワードなど)の管理は、脆弱性スキャンと同様に極めて重要です。

誤用例(絶対に行ってはならないこと):

# Dockerfile内にハードコードされたAPIキー (イメージレイヤーに残存)


# FROM ubuntu:latest


# ENV API_KEY="sk_live_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"


# RUN echo "Hardcoded secret exposed!" > /app/secret.txt


# CMD ["bash"]

これは、イメージが漏洩した場合に秘匿情報が直接露呈する最悪のパターンです。ビルド時の--build-argを使用しても、履歴レイヤーに残る可能性があるため、基本的には避けましょう。

安全な代替: 秘匿情報は、コンテナイメージには含めず、実行時に外部から安全に供給するのが鉄則です。

# 例: Kubernetes Secrets を使用して環境変数として供給、またはファイルとしてマウント


# 1. Secretの作成 (例: base64エンコードされた秘密の値)


# kubectl create secret generic my-app-secret --from-literal=api-key='sk_live_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'

#


# 2. Pod定義でSecretを使用 (my-app-secretというSecretからapi-keyを環境変数MY_API_KEYに設定)

apiVersion: v1
kind: Pod
metadata:
  name: my-app-pod
spec:
  containers:

  - name: my-app-container
    image: myrepo/my-app:latest
    env:

    - name: MY_API_KEY # 環境変数名
      valueFrom:
        secretKeyRef:
          name: my-app-secret # 参照するSecretの名前
          key: api-key        # Secret内のキー
    volumeMounts:

    - name: secret-volume
      mountPath: "/etc/secrets" # Secretをファイルとしてマウントするパス
      readOnly: true
  volumes:

  - name: secret-volume
    secret:
      secretName: my-app-secret # マウントするSecretの名前
      items:

      - key: api-key            # Secret内のキー
        path: api-key.txt       # マウントされるファイル名 (例: /etc/secrets/api-key.txt)
  • 鍵のローテーション: Kubernetes SecretsやAWS Secrets Manager、Azure Key Vault、HashiCorp Vaultなどのシークレット管理サービスを活用し、秘匿情報を定期的に自動ローテーションする仕組みを導入します。

  • 最小権限: 秘匿情報へのアクセスは、その情報が必要な特定のサービスアカウントやIAMロールにのみ、必要最小限の権限で付与します。

  • 監査: 秘匿情報へのアクセスログを記録し、異常なアクセスパターンがないか継続的に監視・監査します。

現場の落とし穴

  • 誤検知 (False Positive): スキャンツールは、開発者が「利用されていないコードパス」や「外部から到達不能な機能」にある脆弱性を報告することがあります。これを全て修正しようとすると、開発速度が著しく低下します。対策としては、除外リストの慎重な運用と定期的なレビュー、およびリスク評価に基づいた優先順位付けが重要です。

  • 検出遅延: スキャンがビルドプロセスの最終段階に組み込まれていると、問題が発覚した際の手戻りが多く、修正コストが高くなります。開発の早い段階(例: PR作成時、ローカルビルド時)にスキャンを導入することで、早期発見・早期修正を促します。

  • 可用性トレードオフ: 厳しすぎるスキャンポリシーは、クリティカルではない脆弱性でさえデプロイをブロックし、サービスの可用性や開発速度に影響を与える可能性があります。ビジネスリスクとセキュリティリスクのバランスを見極め、段階的なポリシー適用や、緊急時のバイパス手順(監査あり)を検討します。

  • ベースイメージ更新の追従: ベースイメージの脆弱性パッチは頻繁にリリースされますが、その都度すべてのイメージを再ビルド・再デプロイするのは運用負荷が高いです。自動化されたイメージリビルドパイプラインの構築や、レジストリによる定期的な再スキャンで、最新の脆弱性情報をキャッチアップする仕組みが必要です。

まとめ

コンテナイメージ脆弱性スキャンは、コンテナ化されたアプリケーションのセキュリティを確保するための基盤です。CI/CDパイプラインへの組み込み、信頼できるベースイメージの選択、秘匿情報の適切な管理、そして継続的な運用改善を通じて、変化の速いコンテナ環境におけるセキュリティリスクを効果的に低減できます。単にツールを導入するだけでなく、ポリシー策定、自動化、そしてチーム全体でのセキュリティ意識の向上が、堅牢なシステムを構築する鍵となります。

文書情報

記事タイトル
コンテナイメージ脆弱性スキャン入門
作成日
更新日
Source URL
https://papanda925.com/?p=3407

ライセンス: 本記事のうち、当サイトが権利を有する本文・自作図表は、特記なき限り CC BY 4.0 で利用できます。生成AIを活用して作成・編集した内容を含みます。コードについて、別途ライセンス表示またはリンク先GitHubリポジトリのライセンスがある場合は、その条件を優先します。引用・第三者資料・画像・商標等は本ライセンスの対象外です。 利用ポリシー

タイトルとURLをコピーしました