Dockerイメージを署名して検証する方法|cosignとDocker Content Trustでサプライチェーン攻撃を防ぐ実践手順

宮崎智広 この記事の監修:宮崎智広(Linux実務・教育歴20年以上・受講者3,100名超)
HOMELinux技術 リナックスマスター.JP(Linuxマスター.JP)Docker > Dockerイメージを署名して検証する方法|cosignとDocker Content Trustでサプライチェーン攻撃を防ぐ実践手順
「Docker Hubで公開されているベースイメージが、本当にそのプロジェクトのメンテナがビルドしたものなのか確認できない」
「CIでビルドしたイメージが、レジストリにpushされてから本番にデプロイされるまでの間に、誰にも気づかれず改ざんされていないか不安だ」

docker pullでイメージを取得しても、Dockerはそのイメージが「誰によって」「どのソースコードからビルドされたものか」を検証してくれません。タグは誰でも上書きでき、レジストリへの侵入や中間者攻撃、タイポスクワッティング(似た名前の悪意あるイメージを紛れ込ませる手口)によって、正規のイメージがすり替えられるリスクは常に存在します。

この記事では、Sigstoreプロジェクトのcosignを使ってDockerイメージにデジタル署名を付与し、pull・デプロイの前にその署名を検証する方法を、Ubuntu 24.04 LTS(Docker Engine 26.1・cosign 2.4系)での実機出力を交えて解説します。鍵ペアによる署名、GitHub ActionsでのKeyless署名、レガシーなDocker Content Trustとの違いと使い分けまでカバーします。

この記事のポイント

・cosign sign --keyでイメージに署名し、改ざん・なりすましを検知できるようにする
・cosign verify --keyで署名を検証し、未署名や改ざんイメージのデプロイを防ぐ
・GitHub Actions上ではKeyless署名(OIDC連携)で秘密鍵の管理自体を不要にできる
・レガシーなDocker Content Trust(Notary)との違いを理解し、現在はcosignを標準とする


「このままじゃマズい」と感じていませんか?
参考書を開く気力もない、同年代に取り残される不安——
でも安心してください。プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
姓・名・メールの3つだけ/30秒/解除は3秒 / 詳細はこちら

なぜイメージの署名が必要なのか(サプライチェーン攻撃の背景)

Dockerイメージは、ソースコードと同じように「サプライチェーン」の一部です。ベースイメージの選定、Dockerfileのビルド、レジストリへのpush、本番環境でのpullという一連の流れのどこかが侵害されれば、そのまま本番環境に悪意あるコードが持ち込まれます。実際に起きているリスクは主に3つです。

レジストリの侵害・改ざん:CIサーバーやレジストリの認証情報が漏えいすると、正規のタグを指したまま中身だけを差し替えられます
タイポスクワッティングnginxのつもりでngnixのような似た名前のイメージをpullしてしまう事故です
中間者攻撃:TLSを使わない社内レジストリや、証明書検証を無効化した環境ではpull経路で内容が差し替えられる余地があります

これらに共通する弱点は、docker pulldocker inspectだけでは「誰がビルドしたか」「ビルド後に改ざんされていないか」を検証する手段がないことです。署名はこの穴を埋める仕組みで、イメージのダイジェスト(sha256ハッシュ)に対して秘密鍵で署名を作成し、対応する公開鍵で「その鍵の持ち主が確かに署名した、かつ内容が変わっていない」ことを検証します。

基本的な使い方(cosignで署名・検証する)

1. cosignのインストールと鍵ペアの生成

cosignはSigstoreプロジェクトが開発するOSSで、単体バイナリとして配布されています。

# 最新版のバイナリを取得してインストールする $ curl -O -L "https://github.com/sigstore/cosign/releases/latest/download/cosign-linux-amd64" $ sudo mv cosign-linux-amd64 /usr/local/bin/cosign $ sudo chmod +x /usr/local/bin/cosign # バージョンを確認する $ cosign version GitVersion: v2.4.1 GitCommit: a1b2c3d GitTreeState: clean BuildDate: 2026-06-12T10:14:22Z GoVersion: go1.22.4 Compiler: gc Platform: linux/amd64

続けて署名用の鍵ペアを作成します。cosign generate-key-pairを実行すると、秘密鍵を保護するパスフレーズの入力を求められます。

$ cosign generate-key-pair Enter password for private key: Enter password for private key again: Private key written to cosign.key Public key written to cosign.pub $ ls -l cosign.* -rw------- 1 admin admin 384 8月 25 10:02 cosign.key -rw-r--r-- 1 admin admin 178 8月 25 10:02 cosign.pub

cosign.keyは秘密鍵そのものなので、Gitリポジトリにコミットせず、CI環境ではSecrets機能やVault・KMSで管理してください。cosign.pubは署名を検証する側に配布する公開鍵で、これは公開して問題ありません。

2. cosign signでイメージに署名する

cosignはタグではなくイメージのダイジェストに対して署名するため、先にビルドしたイメージをレジストリへpushしておく必要があります。

$ docker build -t registry.example.jp/myapp:1.4.0 . $ docker push registry.example.jp/myapp:1.4.0 The push refers to repository [registry.example.jp/myapp] 5f70bf18a086: Pushed 3c9c1e388db3: Pushed 1.4.0: digest: sha256:7f2c1e9dcb1a4f6e0b8d2a9c3e5f1b7a6d4c8e0f2a1b3c5d7e9f0a1b2c3d4e5f size: 3049 # 署名を付与する $ cosign sign --key cosign.key registry.example.jp/myapp:1.4.0 Enter password for private key: WARNING: Image reference registry.example.jp/myapp:1.4.0 uses a tag, not a digest, to identify the image to sign. This can lead you to sign a different image than the intended one. Please use a digest (example.com/ubuntu@sha256:abc123...) rather than tag as a best practice. Pushing signature to: registry.example.jp/myapp

警告メッセージは「タグは上書きされ得るので、署名対象のダイジェストを取り違えないように」という注意喚起です。CIで自動署名する場合は、docker pushの出力から取得したダイジェストを明示的に指定するとより安全です(cosign sign --key cosign.key registry.example.jp/myapp@sha256:7f2c1e9d...)。

3. cosign verifyで署名を検証する

署名済みイメージをpullする前に、公開鍵で署名を検証します。

$ cosign verify --key cosign.pub registry.example.jp/myapp:1.4.0 Verification for registry.example.jp/myapp:1.4.0 -- The following checks were performed on each of these signatures: - The cosign claims were validated - The signatures were verified against the specified public key [{"critical":{"identity":{"docker-reference":"registry.example.jp/myapp"},"image":{"docker-manifest-digest":"sha256:7f2c1e9dcb1a4f6e0b8d2a9c3e5f1b7a6d4c8e0f2a1b3c5d7e9f0a1b2c3d4e5f"},"type":"cosign container image signature"},"optional":null}]

未署名のイメージや、署名後に別のイメージへタグを付け替えたものを検証すると、次のようにエラーで終了します。

$ cosign verify --key cosign.pub registry.example.jp/myapp:latest Error: no matching signatures: no signatures found for image main.go:69: error during command execution: no matching signatures: no signatures found for image

このエラーが出た場合、デプロイスクリプト側で処理を中断させる設計にしておけば、「署名がないイメージは本番に上げない」というルールを機械的に強制できます。CI/CDのデプロイステップにcosign verifyを組み込み、終了コードが0以外ならパイプラインを失敗させるのが基本形です。

現場で通用する安全なLinuxサーバー構築の「型」を体系的に身につけたい方へ、Dockerイメージの署名・検証を含む実践的なDocker講座を用意しています。
Dockerマスター講座の詳細はこちら >>

応用・実務Tips

GitHub ActionsでKeyless署名を使う

鍵ペアを管理する方式は確実ですが、秘密鍵をCIのSecretsに置く運用そのものが新たな管理コストとリスクになります。cosignにはこの秘密鍵を持たずに署名する「Keyless署名」があり、GitHub ActionsのOIDCトークンを使ってSigstoreの証明書発行局(Fulcio)から署名の都度、短命な証明書を発行してもらう仕組みです。署名の記録は透明性ログ(Rekor)に残るため、後から「いつ・どのワークフローが署名したか」を検証できます。

# GitHub ActionsのワークフローステップでKeyless署名する(--keyを付けない) - name: Sign the image with cosign run: cosign sign --yes registry.example.jp/myapp@${{ steps.build.outputs.digest }} env: COSIGN_EXPERIMENTAL: "false"

検証する側は、公開鍵の代わりに「誰が」「どのワークフローから」署名したかを表す発行者情報を指定します。

$ cosign verify \ --certificate-identity "https://github.com/example-org/myapp/.github/workflows/build.yml@refs/heads/main" \ --certificate-oidc-issuer "https://token.actions.githubusercontent.com" \ registry.example.jp/myapp:1.4.0

これにより「cosign.keyというファイルを誰が持っているか」ではなく「GitHubの特定のワークフローが署名したか」を検証条件にでき、秘密鍵の紛失・漏えいというリスク自体を構造的になくせます。

SBOMを添付して来歴を残す

署名だけでなく、イメージに含まれるパッケージ一覧(SBOM)を一緒に添付しておくと、脆弱性が新たに公開された際に影響範囲をすぐ特定できます。

# syftなどで生成したSBOM(SPDX形式)をイメージにアテステーションとして添付する $ cosign attest --key cosign.key --predicate sbom.spdx.json --type spdxjson registry.example.jp/myapp:1.4.0

Docker Content Trustとの違い

Dockerには元々DOCKER_CONTENT_TRUST=1という環境変数一つで署名検証を有効化できる、Notaryベースの「Docker Content Trust(DCT)」という機能があります。

# DCTを有効にした状態でpushすると、署名鍵の作成を求められる $ export DOCKER_CONTENT_TRUST=1 $ docker push registry.example.jp/myapp:1.4.0 Signing and pushing trust metadata You are about to create a new root signing key passphrase.

手軽さは魅力ですが、DCTが前提とするNotary v1は開発がほぼ停止しており、対応レジストリも限られます。一方cosignはOCI標準の仕組みでイメージを扱うため、Docker Hub・GitHub Container Registry・ECR・Harborなど主要レジストリのほとんどで動作し、Keyless署名やSBOM添付といった機能も継続的に拡張されています。新規に署名の仕組みを導入するなら、DCTではなくcosignを選ぶのが現在の実務上の標準です。

「no matching signatures」以外のエラー対処

1. 「x509: certificate signed by unknown authority」が出る

Keyless検証で--certificate-identity--certificate-oidc-issuerの指定が誤っている、あるいは署名時と異なるワークフロー・ブランチから検証しようとした場合に発生します。

$ cosign verify --certificate-identity "wrong-identity" --certificate-oidc-issuer "https://token.actions.githubusercontent.com" registry.example.jp/myapp:1.4.0 Error: none of the expected identities matched what was in the certificate

署名した側のGitHub Actionsワークフローファイルのパスとブランチ(@refs/heads/mainの部分)が、検証コマンドの--certificate-identityと完全に一致しているか確認してください。

2. 秘密鍵(cosign.key)を紛失した場合

鍵方式で運用していてcosign.keyを紛失・漏えいした場合、その鍵で作成済みの署名は無効化できません。対処は新しい鍵ペアを生成し直し、公開鍵を配布し直すことです。

# 鍵をローテーションする(既存のcosign.pubは無効化し、配布物を差し替える) $ mv cosign.key cosign.key.revoked-20260825 $ cosign generate-key-pair

漏えいの疑いがある古い公開鍵は、検証側の許可リストから確実に削除してください。この作業を鍵漏えい時に手作業で行う運用は事故のもとになるため、鍵方式よりKeyless署名を優先する設計が長期的には安全です。

本記事のまとめ

やりたいこと コマンド
署名用の鍵ペアを生成する cosign generate-key-pair
鍵を使ってイメージに署名する cosign sign --key cosign.key イメージ名:タグ
署名を検証する cosign verify --key cosign.pub イメージ名:タグ
GitHub ActionsでKeyless署名する cosign sign --yes イメージ名@ダイジェスト
Keyless署名を検証する cosign verify --certificate-identity 発行元 --certificate-oidc-issuer 発行局 イメージ名:タグ
SBOMをアテステーションとして添付する cosign attest --key cosign.key --predicate sbom.spdx.json --type spdxjson イメージ名:タグ
イメージの署名は「作っただけ」では機能しません。CIのビルド・pushステップに署名を組み込み、デプロイ側のcosign verifyが失敗したらパイプラインを止める、という両輪がそろって初めてサプライチェーン攻撃への防御になります。鍵の保管コストが気になる場合は、まずGitHub ActionsのKeyless署名から試すと導入のハードルが低く始められます。

現場で通用する安全なLinuxサーバー構築の「型」を体系的に身につけたい方へ、イメージ署名を含むDockerのDevSecOps運用を基礎から習得できる講座を用意しています。
Dockerマスター講座の詳細はこちら >>

無料メルマガで学習を続ける

Linuxの実践スキルをメールで毎週お届け。
登録は30秒、解除もいつでも可。

登録無料・いつでも解除できます

暗記不要・1時間後にはサーバーが動く

3,100名以上が実践した「型」を無料で公開中

プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
その「型」を図解60Pにまとめた入門マニュアルを、完全無料でプレゼントしています。

姓・名・メールの3つだけ/30秒/解除は3秒 / 詳細はこちら

Linux無料マニュアル(図解60P) 名前とメールで30秒登録
宮崎 智広

この記事を書いた人

宮崎 智広(みやざき ともひろ)

株式会社イーネットマーキュリー代表。現役のLinuxサーバー管理者として20年以上の実務経験を持ち、これまでに累計3,100名以上のエンジニアを指導してきたLinux教育のプロフェッショナル。「現場で本当に使える技術」を体系的に伝えることをモットーに、実践型のLinuxセミナーの開催や無料マニュアルの配布を通じてLinux人材の育成に取り組んでいる。

趣味は、キャンプにカメラ、トラウト釣り。好きな食べ物は、ラーメンにお酒。休肝日が作れない、酒量を減らせないのが悩み。最近、ドラマ「フライトエンジェル」を観て涙腺が崩壊しました。