👽

Dockerイメージの安全性を高める10のセキュリティハック

2025/03/05に公開

はじめに

本記事は、Snykの『10 Docker Image Security Best Practices』という記事を参考にし、学習の一環としてまとめたものです。
記事の内容をより分かりやすく整理し、個人的な考察や追加情報も加えています。
読者の皆様の学習の一助となれば幸いです。
https://snyk.io/jp/blog/10-docker-image-security-best-practices/

Dockerのセキュリティとは

Snykの記事では、Dockerのセキュリティとは、Dockerコンテナのビルドランタイムオーケストレーションに関する側面として説明しています。
これには、Dockerベースイメージのセキュリティ対策、ユーザー権限の管理、Dockerデーモンの設定、コンテナのCPU制御など、ランタイムにおけるセキュリティ対策が含まれます。
さらに、大規模なDockerコンテナのオーケストレーションに関する課題にも対応する必要があります。

では実際にどのように対応していくのか、10項目のベストプラクティスを通して、具体的な対策を見ていきます!

Dockerのセキュリティに関する10項目のベストプラクティス

1. ベースイメージの最小化

Dockerコンテナの一般的なセキュリティ課題の一つに、イメージサイズの肥大化 があります。
多くのプロジェクトでは、FROM nodeなど一般的なベースイメージをそのまま使用して開発を始めることが多いですが、その際には注意が必要です。
例えば、nodeイメージは、内部的にDebian BookwormAlpine をベースにしたバリアントが提供されており、選択するベースイメージによってサイズや含まれるライブラリが大きく異なります。
もし、プロジェクトで一般的なシステムライブラリやユーティリティが不要であれば、フルバージョンのOSをベースにするのは避けるべきです。一方、node:21-alpine などの Alpineベースのイメージは、軽量で余分なパッケージが含まれていないため、よりセキュアな運用が可能です。

Dockerの公式セキュリティアドバイザリ(2024年)によると、最近のDocker関連ツール(runc, BuildKit, Moby など)において複数の脆弱性が報告されています。これには、特権昇格やリモートコード実行につながる深刻な問題が含まれています。
また、Docker Hub上の公式イメージであっても脆弱性がゼロとは限らず、常に最新のスキャン結果を確認する必要があります。例えば、Node.jsの最新公式イメージでは、node:latestにもいくつかのCVE(Common Vulnerabilities and Exposures)が含まれていることが確認できます。

InfoQの記事 によると、2021年の調査では、約400万のDocker Hubイメージのうち、51%以上に悪用可能な脆弱性が含まれていることが報告されています。

IoT OT Security Newsの記事では、2023年の調査によると、数百種類の一般的なコンテナイメージに、高リスクの脆弱性が多数含まれていることが明らかになりました。特に、悪用手法がすでに公開されている脆弱性も含まれており、一部では実際に攻撃へと発展したケースも報告されています。

以上の調査からわかるように、多くのDocker Hubイメージは、そのまま使用すると既知の脆弱性を含んでおり、セキュリティリスクが高いことが伺えます。
プロジェクトの実行に必要なシステムツールライブラリのみをバンドルする最小限のイメージを使うことを優先することで、攻撃者の攻撃対象領域を最小限に抑え、安全なOSを確実に準備することが可能です。

2. 最小特権ユーザー

デフォルトのDockerfileでは、USERを明示的に指定しない場合、コンテナ rootユーザー で実行されます。
しかし、多くのアプリケーションでは、root権限を必要とするケースはほとんどなく、これがセキュリティ上のリスクとなる可能性があります。

なぜ、rootでの実行が危険なのか?

  • ホストへの影響
    コンテナがホストの名前空間にマップされると、コンテナのrootユーザーがホストシステムのrootユーザーと同じ権限を持つ可能性があります。
  • 攻撃対象の拡大
    コンテナ上のアプリケーションが脆弱な場合、root権限のまま実行すると 特権昇格が容易になり、攻撃の被害が拡大します。
  • マルウェア感染のリスク
    rootユーザーでの実行は、攻撃者にとって都合がよく、マルウェアが悪用しやすくなります。

最小権限での実行

セキュリティリスクを抑えるためには、コンテナ内のアプリケーションを最小限の特権で実行することが重要です。
そのため、専用のユーザーとグループを作成し、USERディレクティブを使用してroot権限なしで動作するように設定しましょう。
以下のDockerfileを最小限の特権で実行できる状態にしてみましょう。

USERを指定しない場合(危険な例)

Dockerfile
FROM ubuntu:latest

# アプリケーション用のディレクトリを作成
RUN mkdir /app

# 作業ディレクトリを設定
WORKDIR /app

# アプリケーションファイルをコピー
COPY . /app

# root ユーザーのまま実行
CMD ["node", "index.js"]

❌ このままだと…

  • すべてのプロセスがrootユーザーとして実行される
  • コンテナが攻撃されると、ホストシステムの制御を奪われる可能性がある
  • 不要な特権が与えられている ため、攻撃対象が広がる

専用ユーザーを作成し、最小特権で実行する

Dockerfile
FROM ubuntu:latest

# アプリケーション用のディレクトリを作成
RUN mkdir /app

# "appuser" という専用のグループとユーザーを作成(ログイン不要のシェル設定)
RUN groupadd -r appgroup && useradd -r -s /usr/sbin/nologin -g appgroup appuser

# 作業ディレクトリを設定
WORKDIR /app

# アプリケーションファイルをコピー
COPY . /app

# ユーザー権限を変更(アプリケーションディレクトリの所有者を "appuser" に設定)
RUN chown -R appuser:appgroup /app

# 最小特権ユーザーで実行
USER appuser

# アプリケーションの実行コマンド
CMD ["node", "index.js"]

✅ この設定により…

  • appuserユーザーで実行され、root権限なしで動作する
  • コンテナが侵害されても、ホストシステムに影響を与えるリスクを軽減
  • 攻撃対象領域を最小化し、特権昇格を防ぐ

Node.jsalpineイメージで運用する場合、nodeという専用ユーザーがすでにバンドルされています。
以下は一般的なノードユーザーを使用したNode.jsの例です。

Dockerfile
FROM node:21-alpine 
WORKDIR /app
COPY . /app
RUN chown -R node:node /app
USER node
CMD ["node", "index.js"]

Node.js アプリケーションを開発している場合は、公式のDockerおよびNode.jsのベストプラクティスを参照することをお勧めします。

3. イメージ署名と検証によってMITM攻撃を緩和する

Dockerイメージの真正性を保証することはセキュリティ上の重要な課題です。
本番環境で実行されるコンテナの元となるイメージが、信頼できる発行者によって提供されたものであることを確認することは不可欠です。
しかし、イメージが改ざんされるリスクはさまざまな経路で発生する可能性があります。

Dockerイメージの改ざんが発生するケース

1. MITM(Man-in-the-middle)攻撃

  • Dockerクライアントレジストリの通信が盗聴・改ざんされ、悪意のあるイメージが配布される

2. 不正なイメージのプッシュ

  • 攻撃者がレジストリアカウントを乗っ取り、正規のリポジトリにマルウェアを仕込んだイメージをプッシュする

3. 信頼できないソースからのイメージ取得

  • Docker Hubや他のレジストリで、悪意のある第三者が正規のイメージに見せかけた偽のイメージをアップロードする

このような攻撃を防ぐために、Dockerイメージの署名と検証 を適切に行うことが重要になってきます。


Dockerイメージを検証する

Dockerでは、デフォルトではイメージの真正性を検証せずにプルすることができます。
このため、発信元や作成者が不明な恣意的なDockerイメージが利用されるリスクがあります。
本番環境でのセキュリティを確保するためには、ポリシーに関係なく、イメージを取得する前にその真正性を検証することがベストプラクティスです。

Docker Content Trust(DCT)を利用した検証

DockerではDocker Content Trust(DCT) を利用することで、署名付きイメージのみをプル・プッシュ可能にすることができます。
従来はNotary v1に基づいていましが、現在では Notary v2 への移行が進んでおり、DockerはDCTの強化を進めています。

Notary v2 とは?

従来のNotary v1よりも軽量かつ高速に署名検証ができます。
Sigstore (Cosign)との統合が進んでおり、今後の標準的な署名方法になりそうです。
現在、DockerはNotary v2(Sigstoreベース)へ移行を進めていますが、DCTを活用したセキュアな運用は引き続き有効です。

署名付きイメージをプッシュする

以下のコマンドを使用して、一時的に DCT を有効にし、イメージの署名とプッシュを行います。

sh
# DCT を有効化
export DOCKER_CONTENT_TRUST=1

# 署名付きイメージのプッシュ
docker push myaccount/myimage:1

実行結果(署名の作成)

sh
The push refers to repository [docker.io/myaccount/myimage]
64ab2aa20cf9: Layer already exists
b99937123ca0: Layer already exists
...
orig: digest: sha256:c221d4dc80b8a0d3866602020b09722d942157c720273d325a0496a529b5fcab size: 4093
Signing and pushing trust metadata
You are about to create a new root signing key passphrase. This passphrase
will be used to protect the most sensitive key in your signing system.
...
Successfully signed docker.io/myaccount/myimage:1

このプロセスでは、Dockerがイメージの署名を作成し、署名付きメタデータをプッシュします。
また、ルートキーやリポジトリキーのパスフレーズを設定することで、署名情報を安全に管理できます。

未署名イメージのプルを防ぐ

それでは、未署名のイメージをプルしようとした場合を考えてみましょう。
DCTを有効にした状態で、署名されていないイメージをプルすると、リクエストは拒否され、イメージの取得がブロックされます。

sh
export DOCKER_CONTENT_TRUST=1
docker pull untrusted/image:latest

❌ 実行結果

sh
# 未署名イメージの拒否
Error: remote trust data does not exist for image tag 'untrusted/image:latest'

この仕組みにより、未署名イメージの誤使用リスクを軽減し、イメージが真正な発行者によってプッシュされた可能性を高めることができます。

Dockerイメージへの署名

Dockerイメージの出所と真正性を保証するためには、署名付きの信頼できるイメージを優先することが重要です。
特に、Docker Hubによって精査・認定された公式イメージやDocker認定のイメージを利用することで、安全性が向上します。
しかし、それだけでは完全に改ざんリスクを排除できるわけではありません。
そのため、Docker Notaryを活用してイメージに署名し、改ざん検知を行うことで、さらなるセキュリティの強化が可能です。
先ほど出てきたDCT(Docker Content Trust) の仕組みは、このNotaryという機能の上で実装されているみたいですね。


Docker Notaryを使用したイメージ署名

Dockerは、Notary を使用してイメージに署名し、追加の保護レイヤーを提供します。
Notaryによる署名を有効にすると、署名が無効なイメージの実行がブロックされ、改ざんMITM攻撃を防ぐことが可能になります。

では、実際に署名の手順について見ていきましょう。
Docker イメージの署名手順
Docker Content Trust(DCT)が有効な場合、イメージをビルド・プッシュすると自動的に署名が付与されます。

sh
# Docker Content Trust を有効化
export DOCKER_CONTENT_TRUST=1

# イメージをプッシュ(Notary による署名付き)
docker push myaccount/myimage:1

この処理により、初回の署名時にユーザーの秘密鍵が生成され、~/docker/trust に保存されます。
この秘密鍵は、以降のイメージ署名時に使用され、信頼性を担保します。
💡 詳細な手順については、Dockerの公式ドキュメントを参照してください。


NotaryGPGの違い

Dockerイメージの署名には Docker Notaryを活用する方法と GPG(GNU Privacy Guard) を使用する方法がありますが、これらにはどのような違いがあるのでしょうか。
以下が簡単にまとめたものになります。

  • Docker Notary(DCT)

    • イメージの改ざん防止に特化しており、署名付きイメージのみをプル・実行可能 にすることで、MITM攻撃や不正なイメージの利用を防ぐ
    • レジストリレベルでの信頼管理を行い、コンテナ環境に特化したセキュリティ対策を提供
    • リプレイ攻撃(過去の正規な署名データを悪用する攻撃)に対して脆弱性がある可能性 あり
  • GPG(GNU Privacy Guard)

    • 個別のファイルやデータの真正性を検証するためのツールであり、リプレイ攻撃の防止に優れる
    • ただし、Docker イメージの署名やプル時の検証を直接管理する機能はない

より詳細な説明は引用・参考文献の動画をご覧ください。

4. オープンソースの脆弱性を検知、修正、監視する

Dockerコンテナのベースイメージを選択する際は、単に動作するだけでなく、セキュリティのリスク にも注意を払う必要があります。
ベースイメージには、以下のようなセキュリティ上の懸念が含まれている可能性があります

  • 不適切なデフォルト設定(OSのセキュリティ機能が無効化されているなど)
  • 不要なシステムライブラリ(未使用のパッケージが含まれ、脆弱性を持つ可能性がある)

これらに対応するために最初にやるべきことは、問題が発生せずにアプリケーションを実行できるようにすると同時に、可能な限り最小化したベースイメージを選択(alpine, distroless, scratchなど)することです。
これをすることで、不要なライブラリを含まないで済むので、攻撃対象を最小化することが可能です。

しかし、これだけでは十分ではありません。
ベースイメージを最適化しても、将来的に新たな脆弱性が発見される可能性があります。
したがって、継続的な監査とセキュリティチェックが不可欠です。

継続的な監査とセキュリティチェック

定期的に イメージの脆弱性をスキャン し、既知の問題を検出・修正しましょう。
以下に、オープンソースセキュリティソフトウェアの脆弱性から保護するツールを採用し、使用中のすべての Dockerイメージレイヤーに存在するかもしれない脆弱性に対して継続的なDockerセキュリティスキャン監視を追加することです。

🔍 脆弱性スキャンツールの例

Trivy(軽量で高速なコンテナスキャナー)

Trivyは、Aqua Securityによって開発された軽量 & 高速な脆弱性スキャナーです。
コンテナイメージだけでなく、ホスト・リポジトリ・SBOMなどもスキャン可能なのが特徴です。
またセキュリティスキャンの実行は以下のコマンドで実行できます。

trivy image myapp:latest

実行結果は、脆弱性の詳細と重要度(CRITICAL, HIGH, MEDIUM, LOW)が表示されます。

Snyk(詳細なレポートと修正提案がある)
snyk container test myapp:latest

Snyk を使用した、Dockerイメージのスキャン例が以下の通りです。
このDockerfileに対して、snykコマンドを使用します。

Dockerfile
FROM node:21-alpine

RUN mkdir /usr/src
RUN mkdir /usr/src/goof
RUN mkdir /tmp/extracted_files

COPY . /usr/src/goof
WORKDIR /usr/src/goof

RUN npm update
RUN npm install

EXPOSE 3000

CMD ["npm", "start"]
snyk container test node:21-alpine --file=Dockerfile

こちらのコマンドを実行する、以下の情報が得られます。

  • ✅ スキャン対象情報
    • Dockerイメージ名 (node:21-alpine)
    • OSの種類 (alpine)
    • アーキテクチャ (linux/amd64)
  • ✅ 検出された脆弱性
    • 影響を受けるパッケージ(例: openssl, libc-bin)
    • CVE ID(例: CVE-2022-23219)
    • 深刻度(High / Medium / Low)
    • 修正可能なバージョン
  • ✅ 推奨される修正方法
    • 修正バージョンのアップグレード案内
  • ✅ スキャン結果の概要
    • 検出された脆弱性の数
    • スキャンされたパッケージ数
    • Snyk の詳細レポート取得案内(--json オプションあり)
Docker公式の脆弱性スキャン

こちらを使用する場合、Docker Hubにログインする必要があります。

🔨 Docker Scoutのセットアップ

  1. Docker Hubにログイン
docker login

→ 成功するれば、認証情報がローカルに保存されます。


2. イメージのタグ付け
ビルドしたイメージを、Docker Hub のリポジトリ用のタグに変更します。

docker tag demo-container <your-dockerhub-username>/demo-container
  • ✅ docker tag <ローカルのイメージ名> <Docker Hub のリポジトリ名>

  1. Docker Hubへpush
docker push demo-container <your-dockerhub-username>/demo-container

こちらのコマンドで、Docker Hubにdemo-containerイメージがpushされます!
Docker Hubのリポジトリページで確認することができます。

Sigstore(Cosign でコンテナ署名と検証)

Sigstoreは、オープンソースのソフトウェアサプライチェーンのセキュリティ強化を目的としたプロジェクトで、その中の Cosign を使うことで、コンテナイメージにデジタル署名を付与し、後から検証できるようになります。

🔨 Cosignのセットアップ

  1. Cosignのインストール
    Cosignのインストールは、以下のページを参照してください。
    https://docs.sigstore.dev/cosign/system_config/installation/

  2. コンテナイメージの署名
    以下の手順で署名が行えます。
    ✅ 署名用の鍵ペアを生成
cosign generate-key-pair

こちらのコマンドで、次の2つのファイルが生成されます。

  • cosign.key(秘密鍵)
  • cosign.pub(公開鍵)
    これらの鍵はイメージ署名の際に使用します。
    ✅ イメージに署名
cosign sign --key cosign.key <your-dockerhub-username>/sample-app

✅ 署名鍵の検証

cosign verify --key cosign.pub <your-dockerhub-username>/sample-app

成功すると、署名情報が表示されます。

キーレス署名について

Cosignは、従来の「鍵ペアを使用した署名」に加えて、キーレス署名 をサポートしています。
これにより、秘密鍵を管理する必要なく、GitHub ActionsCI/CDなどのワークフローと統合しやすくなっています。

✅ キーレス署名のメリット

  • 秘密鍵の管理不要 → 署名にハードウェアキー(HSM)やKMSを必要とせず、安全性が向上
  • OIDC(OpenID Connect) 対応 → GitHubやGoogle Cloudでのワークフローと連携が容易
  • CI/CDパイプラインとの統合がシンプル → 署名&検証を自動化できる

キーレス署名の主な流れは以下のとおりです

  1. OIDC トークンの取得
  • 開発者は、Google や GitHub などの OpenID Connect (OIDC) プロバイダーを通じて認証を行い、OIDC トークンを取得します
  1. 一時的な鍵ペアの生成
  • Cosignは、取得した OIDCトークンを使用して短期間(約10分間有効)の鍵ペアを生成します​
  1. 署名の実行
  • ​生成された一時的な鍵ペアを使用して、コンテナイメージやアーティファクトに署名を行います
  1. 透明性ログへの記録
  • ​署名情報は、Rekorなどの透明性ログに記録され、公開されます。これにより、署名の検証や監査が可能となります

このプロセスにより、秘密鍵の長期的な管理が不要となり、セキュリティリスクが低減されます。

Chainguard(Chainguard Imagesでセキュアなベースイメージを利用)

🐙 Chainguard Imagesとは

通常のコンテナイメージ(例えば alpine, debian, ubuntu)には、多くの不要なパッケージが含まれており、これが セキュリティリスクにつながることがあります。Chainguard は、最小限のパッケージで構成されたセキュアなベースイメージ を提供し、
不要な要素を排除することで コンテナの安全性とサイズの最適化 を両立しています。
つまり、セキュリティ強化とコンテナサイズの縮小に特化したコンテナプラットフォームです。

🔹 Chainguard Imagesの特徴

✅ 不要なソフトウェアを排除したミニマルなデザイン

  • 脆弱性を最小限に抑えた軽量なベースイメージを提供し、不要なパッケージによる攻撃リスクを削減

✅ 自動化ビルドにより、イメージの最新性と利用可能なセキュリティパッチを確保

  • 継続的な更新とパッチ適用により、最新のセキュアな環境を維持

✅ イメージ内のすべてのアーティファクトの出所を証明する高品質のビルドタイム SBOM(ソフトウェア部品表)

  • 透明性のあるソフトウェアサプライチェーン管理を実現し、どのコンポーネントが含まれているか明確化

✅ Sigstore(Cosign)と連携し、イメージの署名・検証が可能

  • イメージの信頼性を保証し、改ざんや不正アクセスを防止

Cosignapko による再現可能なビルド

  • 署名付きの再現可能なイメージ を作成し、一貫性のあるセキュアな環境を確保

詳しくは公式ドキュメントを参考にしてください。

GoのChainguardイメージの環境構築

Dockerfile
FROM cgr.dev/chainguard/go:latest-dev AS builder

WORKDIR /app

COPY go.mod go.sum ./
RUN go mod download

COPY main.go ./  

RUN go build -o myapp .

# 実行ステージ(最小限のベースイメージ)
FROM cgr.dev/chainguard/static:latest

WORKDIR /app

# ビルド済みバイナリのみコピー(余計なファイルを含めない)
COPY --from=builder /app/myapp .

USER testuser:testuser

CMD ["/app/myapp"]

💡 distroless のChainguardイメージについて
通常のGoイメージと違い、Chainguard の go:latest は distrolessなので、シェル (sh) やパッケージマネージャー (apk, aptなど) が含まれていません。
そのため、FROMでは cgr.dev/chainguard/go:latest-dev を指定します。

以下のコマンドでChainguard Images正しく動作するかどうか確認できます。

docker pull cgr.dev/chainguard/go:latest
docker run --rm -it cgr.dev/chainguard/go:latest sh

公式nginx, python, nodeなどの一般的に使われるイメージからミドルウェア用のイメージ、CLI用のイメージについての詳細が載っています。

5. Dockerイメージに秘密情報を漏洩しない

Dockerイメージをビルドする際、SSH秘密鍵APIトークン などのシークレット情報をイメージに含めてしまうと、たとえ削除したとしても、Dockerのレイヤーにキャッシュされ、情報が漏洩するリスクがあります。
セキュリティを確保するために、以下の方法で Dockerイメージに機密情報を含めないようにしましょう。

📌 Dockerfileにシークレット情報を直接書かない

例えば、プライベートリポジトリからコードを取得するためのSSHキーや、APIトークンを環境変数として埋め込むのは危険です。

❌ NGな例

# SSHキーを直接コピー
COPY id_rsa /root/.ssh/id_rsa

🚨 この方法だと、レイヤーにキャッシュされ、キーが残ってしまう
✅ 対策として Dockerfileの外部で管理しましょう


📌 マルチステージビルドを活用

Dockerのマルチステージビルドを使用すると、機密情報を含む中間コンテナを破棄できるため、安全にシークレットを使用できます。
マルチステージビルドの例

Dockerfile
# Stage 1: シークレットを使用するビルド環境
FROM ubuntu as intermediate
WORKDIR /app

# シークレット情報を一時的に利用(中間コンテナ)
COPY secret/key /tmp/
RUN scp -i /tmp/key build@acme/files .

# Stage 2: 本番環境用のクリーンなイメージ
FROM ubuntu
WORKDIR /app

# 中間イメージからアプリケーションのみコピー(シークレットは含まれない)
COPY --from=intermediate /app .

📌 Docker の --mount=type=secret を活用

Dockerの--mount=type=secret機能を使うことで、シークレット情報を安全にマウントできます。
✅一時的にマウントする例

Dockerfile
# syntax = docker/dockerfile:1.0-experimental
FROM alpine

# デフォルトのシークレットのマウント
RUN --mount=type=secret,id=mysecret cat /run/secrets/mysecret

# カスタムパスにマウント
RUN --mount=type=secret,id=mysecret,dst=/foobar cat /foobar

この方法だと、シークレットはキャッシュされずに、一時的にマウントされるので安全です。


📌 再帰コピー(COPY . .)に注意

COPY . .ビルドコンテキスト全体をDockerイメージにコピーするため、機密ファイルが誤ってイメージに含まれる可能性があります。

COPY . .

❌この方法だと.envprivate.keyなどの機密ファイルが含まれる可能性がある!

このような機密ファイルがある場合、.dockerignoreを活用し機密情報を除外しましょう。
.dockerignoreの例

.dockerignore
private.key
.env
appsettings.json

Dockerコンテナをどのように保護するか

本番環境でのセキュリティを確保するために、以下の対策を徹底しましょう。
✅ マルチステージビルドを活用
本番環境用のDockerイメージには、開発用のアセットやシークレット情報を含めないようにする。

✅ セキュリティツールでスキャン
コンテナのセキュリティツールを使用し、本番環境にデプロイする前に脆弱性をスキャンしましょう。


🛠️ スキャンする環境

  • CLIから直接スキャン
  • Docker Hubにあるイメージをスキャン
  • Amazon ECR / Google GCR などのコンテナレジストリをスキャン

6. 不変性を確保するためのタグ管理

Dockerイメージには 複数のタグ(バリアント) を持たせることができます。
その中でもlatestタグは特に一般的ですが、不変ではなく、同じタグが異なるバージョンのイメージを指す可能性があるため、セキュリティリスクが生じます。
この問題を回避し、Docker のセキュリティと安定性を向上させるために、適切なタグ管理を行いましょう。

なぜlatestタグを避けるべきなのか

  1. バージョンが不明確になる
  • latestタグは固定されていないため、いつの間にか新しいバージョンのイメージに更新されてしまう可能性がある
  • ある日突然、意図しない変更が加えられたバージョンを使うことになり、アプリが動かなくなることも
  1. 環境によって異なるバージョンが実行される
  • ローカル環境では古いlatestのイメージを使っているのに、本番環境では新しいlatestのイメージがプルされるといった問題が発生する
  • これにより、環境ごとに動作が異なるという一貫性のない状態になってしまう
  1. 新たな脆弱性が含まれる可能性がある
  • latestタグのイメージはアップデートのたびに内容が変わるため、ある日突然、新しい脆弱性が含まれたイメージを使用してしまう可能性がある
  • 特定の脆弱性を回避したい場合でも、どのバージョンを使っているのか明確でないと対応が難しくなる

では、どうすればいいのか見ていきましょう。

適切なタグ管理で安全な Docker運用を

📌 具体的なタグを使用する

❌ NG例

# 毎回異なるイメージが取得される可能性あり
FROM node:latest

より特定のタグ(例: :8.0.1 や :8.0.1-alpine)を使用し、環境の一貫性を確保しましょう。

✅ 固定バージョンの使用

# 安定したバージョンを指定
FROM node:18.16.0-alpine

📌 SHA-256 ハッシュを活用

特定のタグを使うよりも、SHA-256 参照を使用すれば完全に同じイメージを取得できるため、セキュリティと一貫性がより強固になります。
✅ SHA-256 ハッシュを使用

# 特定のバージョンの完全なハッシュを指定
FROM node@sha256:3b6e9c1a85bd5c4a8d4cb1f79dc9c0191b5d6cd10ecf0a7c89df36f7a0e512a4

📌 イメージのローカルミラーを作成

特定のイメージタグが削除されたり更新されたりすると、それに依存するチームのシステムが動作しなくなるリスク があります。
この問題を回避するために、自分が管理するレジストリ(例: AWS ECR, Google GCR, 自社のプライベートレジストリ)にイメージのローカルミラーを作成することを検討しましょう。
ただし、このアプローチにはレジストリの管理メンテナンスが必要 になるため、運用コストを考慮することが重要です。

7. ADDのリスクを回避し、安全なCOPYを選択

Docker では、ホストマシンからファイルをコピーしてDocker イメージをビルドする際に、COPYADDの2つのコマンドが使用できます。
一見似ていますが、機能の違いがセキュリティに影響を及ぼす可能性があるため、適切に使い分けることが重要です。

ADDとCOPYの違い

コマンド 機能
COPY ローカルファイルを再帰的にコピー(シンプルで安全)
ADD COPY の機能に加え、アーカイブの自動展開 & リモートURLのダウンロードをサポート

もう少し詳しく見ていきましょう。

なぜ COPYを使うべきなのか

✅ COPY を使うべき理由

  • シンプルで明示的 → 意図しない動作を防げる
  • 不要な機能を持たない → 余計なリスクを回避
  • アーカイブの自動展開をしない → Zip爆弾攻撃のリスクを減らせる
  • リモートファイルのダウンロードをしない → MITM(中間者攻撃)を防ぐ

❌ ADD を使うと起こりうるリスク

  • リモートURLを直接指定できるため、悪意のあるコンテンツをダウンロードするリスク
  • Zipファイルを自動展開するため、Zip爆弾や解凍時の脆弱性が発生する可能性
  • 意図しないファイル操作が発生する可能性がある

COPYADD の違いを理解したところで、実際に どのような場面で COPY を使うべきか を見ていきましょう。

📌 ADDが許容されるケース

基本的にはCOPYを推奨しますが、例外として以下の場合のみADDを使用できます。
✅ ローカルアーカイブ(.tar, .gz など)を自動的に展開したい場合

# ローカルの tar.gz ファイルを自動解凍
ADD archive.tar.gz /app/

ただし、手動で解凍できるなら COPY + RUN tarを推奨!

COPY archive.tar.gz /tmp/archive.tar.gz
RUN tar -xzf /tmp/archive.tar.gz -C /app/ && rm /tmp/archive.tar.gz

📌 COPYを使うべきケース

✅ 安全なローカルファイルコピー

COPY app/ /app/

❌ ADD を使う場合(非推奨)

# `ADD` だと余計なリスクが発生
ADD app/ /app/

📌 ADDでのリモートダウンロードの危険性

ADDを使うと、リモートのURLから直接ファイルをダウンロードできてしまうため、MITM攻撃のリスクが高まります
❌ 危険なADDの使い方

ADD https://example.com/file.tar.gz /app/

この方法だと、リモートのファイルが改ざんされるリスクがあります。

✅ RUN wget または RUN curl を使う
リモートファイルをダウンロードする場合は、COPYではなくwgetcurlを使い、ハッシュ検証を行いましょう。

RUN wget -q https://example.com/file.tar.gz -O /tmp/file.tar.gz && \
    echo "3b6e9c1a85bd5c4a8d4cb1f79dc9c0191b5d6cd10ecf0a7c89df36f7a0e512a4  /tmp/file.tar.gz" | sha256sum -c - && \
    tar -xzf /tmp/file.tar.gz -C /app/ && \
    rm /tmp/file.tar.gz

こうすることで、ダウンロードしたファイルが改ざんされていないか検証でます。

8. 適切なメタデータラベルでDockerイメージを整理

Dockerイメージラベルは、イメージに関するメタデータを付与する仕組みです。
適切なラベルを追加することで、イメージの管理が容易になり、ユーザーが使用方法を理解しやすくなります。

なぜメタデータラベルが重要なのか

Docker イメージに メタデータラベルを適切に設定することで、管理の透明性を向上 させ、トラブルを防ぐことができます。
具体的には、以下のメリットがあります。
イメージの作成・管理者情報を明示できる
ビルドやリリース情報を記録し、追跡可能にする
品質やセキュリティの状態を示せる
外部ツール(CI/CD, セキュリティ監査)との連携がしやすくなる

📌 基本的なラベルの追加方法

最も一般的なラベルは maintainer(イメージの管理者情報) です。
以下のように、LABEL コマンドを使用してメタデータを追加できます。
このラベルを追加することで、誰がイメージを管理しているかを明示できます。

LABEL maintainer="me@acme.com"

推奨される追加メタデータ

Dockerイメージには、メンテナンス情報だけでなく、ソースコードの出どころやバージョン管理に関するメタデータを追加することが推奨されます。
以下のような情報を含めることで、管理の透明性が向上し、長期的な運用がスムーズになります。

ラベル名 目的
org.opencontainers.image.source ソースコードのリポジトリ(例: GitHub, GitLab)
org.opencontainers.image.revision コミットハッシュ(どのバージョンかを明示)
org.opencontainers.image.vendor 開発元の企業・組織名
org.opencontainers.image.licenses 使用しているライセンス情報
org.opencontainers.image.version イメージのバージョン情報
org.opencontainers.image.description イメージの説明
securitytxt セキュリティポリシーの参照(SECURITY.TXT の URL)

また以下のように、複数ラベルを追加することで管理しやすいDockerイメージが作成できます。

LABEL maintainer="me@acme.com" \
      org.opencontainers.image.title="MyApp" \
      org.opencontainers.image.description="This is a sample application." \
      org.opencontainers.image.version="1.2.3" \
      org.opencontainers.image.source="https://github.com/acme/myapp" \
      org.opencontainers.image.revision="abc123def456" \
      org.opencontainers.image.licenses="MIT" \
      securitytxt="https://www.example.com/.well-known/security.txt"

📌 security.txtの活用

セキュリティを考慮し、Dockerラベルには責任ある開示ポリシーを示すSECURITY.TXT(RFC 5785)ファイルを含めることがベストプラクティスです。
これはセキュリティ脆弱性の報告先を明示し、透明性を確保するためのものです。

Dockerイメージのラベルの詳細については、Label Schema をご覧ください。

9. マルチステージビルドで小規模かつ安全な Docker イメージを作成する

Dockerfileを使用してアプリケーションをビルドすると、開発ツール・一時ファイル・テスト用のパッケージなど、本番環境には不要なアーティファクトも含まれてしまいます。
これらの不要なデータを残すと、以下の問題が発生します。

不要なアーティファクトがあると起こる問題

❌ Dockerイメージのサイズが肥大化 → ダウンロードやデプロイに時間がかかる
❌ 不要なパッケージが含まれる → 攻撃対象領域(セキュリティリスク)が増える
❌ 本番環境には不要な開発ツールが残る → 実行環境が不必要に複雑化

📌 マルチステージビルドを活用

この問題を解決するためにマルチステージビルドを活用します。
マルチステージビルドを使用すると、ビルドプロセスで作成された一時イメージを削除し、最終的に本番環境に必要なデータだけを含めた小規模なイメージを作成できます。

📌 マルチステージビルドの流れ

マルチステージビルドでは、以下の2つのイメージを作成します。
1. ビルド用のイメージ

  • 開発ツールやテスト環境を含む(コンパイル用の環境)
    2. 本番用の最小イメージ
  • 不要なファイルを削除し、最小構成にする(動作に必要なアーティファクトのみ)

Golangのマルチステージビルド実装例

GolangはGoコンパイラを使って実行可能なバイナリを作成できるため、本番環境ではGoコンパイラを含める必要がありません
そのため、マルチステージビルドを使用すると、最小構成の軽量なイメージを作成できます。

以下のDockerfileの例では、Golangのマルチステージビルドを活用して、最小限の本番用イメージを作成しています。

# ステージ 1: ビルド環境
FROM golang:1.19 AS build
WORKDIR /app

# 依存関係をダウンロード
COPY go.mod go.sum ./
RUN go mod download

# アプリケーションのコードをコピーしてビルド
COPY . .
RUN go build -o myapp

# ステージ 2: 本番用の軽量イメージ
FROM alpine:latest
WORKDIR /app

# ビルドしたバイナリのみをコピー
COPY --from=build /app/myapp .

# 実行コマンド
CMD ["./myapp"]

10. linterの活用

Dockerfileを適切に管理するために、linter(静的解析ツール)を導入しましょう。
linterを使用することで、よくあるミスを自動的に検出し、ベストプラクティスに従った記述ができるようになります。
これは、Dockerfileのセキュリティ問題を静的に分析するDockerセキュリティスキャンの重要な要素でもあります。

linterのメリット

linterには、以下のメリットが挙げられます。
✅ ミスを自動検出し、品質を向上
✅ ベストプラクティスに従ったコードを維持
✅ エンジニア間でのコードの統一性を保つ
✅ セキュリティリスクを未然に防ぐ

こうしたlinterの一つとしてhadolintがあります。(他にも、dockle, Amazon ECR Image scanningがあります)
hadolintを使うことで、Dockerfileを解析し、ベストプラクティスに沿わない記述を警告・修正提案してくれます。
hadolintは以下の方法で導入可能です。

# macOS(Homebrew)
brew install hadolint

# Linux(バイナリをダウンロード)
wget -O /usr/local/bin/hadolint https://github.com/hadolint/hadolint/releases/latest/download/hadolint-Linux-x86_64
chmod +x /usr/local/bin/hadolint

また、VScodeの拡張機能としても利用できます。リアルタイムでDockerfileのエラーを検出できるので便利です。

🔎 hadolint を使ったDockerfileのチェック結果

以下のコマンドを実行することで、Dockerfileの静的解析が行えます。

docker run --rm -i hadolint/hadolint < Dockerfile-test

# 出力結果
/dev/stdin:3 DL4000 MAINTAINER is deprecated
/dev/stdin:6 DL3008 Pin versions in apt get install. Instead of `apt-get install <package>` use `apt-get install <package>=<version>`
/dev/stdin:6 DL3009 Delete the apt-get lists after installing something
/dev/stdin:6 DL3016 Pin versions in npm. Instead of `npm install <package>` use `npm install <package>@<version>`
/dev/stdin:6 DL3015 Avoid additional packages by specifying `--no-install-recommends`
/dev/stdin:17 DL3020 Use COPY instead of ADD for files and folders
/dev/stdin:18 DL3020 Use COPY instead of ADD for files and folders

エラーコードと対応する修正方法が出力されます。

おわりに

ここまで、Snykの記事を基に、Docker のセキュリティベストプラクティスを整理しました。
本記事で整理した 10 項目を意識することで、セキュアで効率的なDocker運用 を実現できます。
また、hadolintなどの静的解析ツールを活用してチューニングし、SnykTrivy で定期的に脆弱性をチェックすることでよりベストプラクティスに則った、安全なイメージを構築することができます。
セキュリティは、一度対策すれば終わりではなく、継続的な監視と改善が求められます。
最新の脆弱性情報やDockerのベストプラクティスを学ぶことで、より安心して使えるコンテナ環境を整えることができます!

引用・参考文献

https://docs.docker.jp/develop/security-best-practices.html

https://snyk.io/jp/blog/10-docker-image-security-best-practices/

https://www.docker.com/ja-jp/blog/docker-best-practices-using-tags-and-labels-to-manage-docker-image-sprawl/

https://www.docker.com/ja-jp/blog/docker-best-practices-using-tags-and-labels-to-manage-docker-image-sprawl/

https://qiita.com/bricolageart/items/b78a68f3003842beeb24

Docker の Content Trust と Notary を使用して Docker イメージに署名することについて

https://snyk.io/jp/blog/securing-container-applications-using-the-snyk-cli/

https://www.cybertrust.co.jp/blog/oss-security/sigstore-code-signing.html

Discussion