【テクニカル・上級編】 コンテナイメージのレジストリ汚染とサプライチェーン攻撃 – オフェンシブセキュリティ & リバースエンジニアリング防御ガイド

コンテナレジストリの深淵:見えないサプライチェーン汚染と「信頼」の再定義

昨今のセキュリティカンファレンスでは「サプライチェーン攻撃」という言葉が飛び交っているが、実際に現場で起きているのは、単なるライブラリの依存関係の不備ではない。レジストリという名の「信頼の保管庫」が、攻撃者にとっての格好のトロイの木馬の搬入口になっているという事実だ。

今日は、表面的な脆弱性スキャンの先にある、コンテナレジストリ汚染と、それを無効化するためのアーキテクチャ設計について、あえて泥臭い視点から切り込んでいこう。

1. レジストリ汚染の解剖:なぜ「タグ」は信用できないのか

多くのエンジニアが犯す最大の過ちは、latest タグや特定のバージョンタグを「不変(Immutable)」だと誤認していることだ。

攻撃者は、レジストリのAPI(Docker Registry V2 API)を悪用し、マニフェストファイルを書き換える。イメージのDigest(SHA256ハッシュ)を固定せずにプルしている場合、クライアントは「同じタグだから同じものだ」と信じ込み、改ざんされたレイヤーを平気で実行する。

ここで重要なのは、「通信プロトコル仕様の欠陥」ではなく「運用の前提条件」の欠落である。HTTP通信の暗号化(TLS)は当然だが、その先の「データの真正性」を保証するレイヤーが抜け落ちているのだ。

2. 署名検証:Cosignによる「信頼の鎖」の構築

単にイメージをスキャンするだけでは、ランタイムでの汚染を防げない。ここで登場するのが Cosign を用いた署名検証だ。

GitHub Actions等でイメージをビルドする際、秘密鍵を用いて署名を付与し、Kubernetesクラスター側で Kyverno や Policy Controller を用いて、署名のないイメージの実行を完全に遮断する。

以下は、Kyverno を用いて「署名のないイメージを一切許可しない」という強制力を働かせるためのポリシー例だ。

# Kyvernoによる検証ポリシーのサンプル
# 署名検証(Cosign)が成功しないイメージのデプロイを拒否する
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: verify-image-signatures
spec:
  validationFailureAction: Enforce  # 検証失敗時は即座にブロック
  rules:
    - name: verify-signature
      match:
        resources:
          kinds:
            - Pod
      verifyImages:
        - imageReferences:
            - "registry.example.com/production/*" # 自社のレジストリを指定
          attestors:
            - entries:
                - keys:
                    publicKeys: |-
                      -----BEGIN PUBLIC KEY-----
                      # ここに公開鍵を配置
                      -----END PUBLIC KEY-----

3. 次世代の脅威:生成AIとガードレイルのアーキテクチャ

最近のレッドチームの活動で顕著なのが、AIによるコード生成を悪用した「プロンプトインジェクション」を、コンテナビルドパイプラインに紛れ込ませる手法だ。

AIが生成したコードの中に、一見無害な curl コマンドが埋め込まれ、実行時に動的に外部から悪意あるバイナリをロードする。これを防ぐには、単純な静的解析では不十分だ。

防御層(ガードレイル)の設計案

1. ネットワークのマイクロセグメンテーション: コンテナが外部の未知のIPと通信することを許可しない。Egress 通信を Istio などのサービスメッシュで制御し、ホワイトリストベースで制限する。
2. eBPFによるランタイム監査: Falco を導入し、コンテナ内部での予期せぬプロセス実行や、特定のファイルパスへの書き込みをカーネルレベルでフックする。

# Falcoのルール例:未知のバイナリが実行されたらアラートを飛ばす
- rule: Unexpected process spawned
  desc: "コンテナ内で許可されていないプロセスが起動した"
  condition: >
    spawned_process and container and 
    not proc.name in (allowed_processes)
  output: "不正なプロセスが実行されました (user=%user.name command=%proc.cmdline container=%container.id)"
  priority: WARNING

4. 耐量子暗号への移行を見据えたアーキテクチャの未来

これからのセキュリティアーキテクトが考えるべきは、数年先に訪れる「量子コンピュータによる暗号解読」への備えだ。現在、私たちが信頼している RSA や ECDSA による署名は、数年後には無力化される可能性がある。

今から実装すべきは、「暗号アルゴリズムの抽象化」である。Cosign は将来的なアルゴリズムの変更に対応可能だが、インフラ側も「署名検証エンジンを容易に切り替えられる」設計にしておく必要がある。秘密鍵の管理をハードウェアセキュリティモジュール(HSM)やKMSに委託し、いつでもアルゴリズムをアップデートできる態勢を整えておくことが、真のレジリエンスだ。

最後に:防御側の心得

セキュリティは「パッチを当てて終わり」の静的な作業ではない。攻撃者は常に、インフラの隙間、設定の甘さ、そして「人間が楽をしたがる心理」を突いてくる。

レジストリ汚染を防ぐことは、単なる技術的な制約ではなく、開発パイプライン全体の「信頼の基点(Root of Trust)」をどこに置くかという哲学の問題だ。今日紹介した Cosign と Kyverno、そして Falco を組み合わせた多層防御を構築し、攻撃者が「ここを攻めるのはコストに合わない」と判断させる状況を作り出すこと。それが、我々レッドチームが最終的に目指すべき防御の形である。

妥協のない設計こそが、最も強力な防衛になることを忘れないでほしい。

コメント

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