Dockerイメージのセキュリティスキャンと修正:Trivy実践チュートリアルとCI/CD統合ガイド
2021年12月10日、監視ダッシュボードのアラート曲線が突然赤く跳ね上がりました。運用グループに投げ込まれたのは、のちに技術界を震撼させるLog4Shell脆弱性のCVE番号:CVE-2021-44228です。十数のマイクロサービスがこの脆弱性を含むベースイメージを使用しており、チームは一晩中、イメージの緊急アップグレードと再デプロイに追われました。
事後の振り返りで、上司から投げかけられたのは「そもそも自分たちが使っているイメージにどんな脆弱性があるのか、なぜ把握できていないんだ?」という問いでした。
NSFOCUS(緑盟科技)の研究によると、Docker Hubの76%のイメージに既知のセキュリティ脆弱性が存在します。今使っている python:3.9 イメージ? nginx:latest? おそらく数十のCVE脆弱性が潜んでいるでしょう。
「イメージ内の脆弱性をどう把握するか」「見つけた脆弱性をどう修正するか」「CI/CDで自動検知し、脆弱性のあるイメージの本番デプロイをどう防ぐか」——これらの疑問に、私は本番環境で2年間試行錯誤して答えを見つけました。
本記事では、以下を手取り足取り解説します。
- TrivyなどのツールでDockerイメージの脆弱性を高速スキャンする方法
- 体系的な脆弱性修正手法(単なる「イメージバージョン更新」以上の内容)
- GitHub Actions、GitLab CIなどのCI/CDプラットフォームへの自動スキャン統合
掲載しているコマンドと設定は、すべてそのままコピーして実行できる実践コードです。読み終えれば、多くの遠回りを避けられるはずです。
"NSFOCUS(緑盟科技)2018年3月の研究レポートによると、Docker Hubから無作為に抽出したイメージの4分の3以上(76%)に既知のセキュリティ脆弱性が存在しました。"
Dockerイメージのセキュリティ脅威の全貌
Dockerイメージにはどのようなセキュリティ問題があるのか?
正直に言うと、コンテナを始めた頃は、Dockerイメージは「パッケージ化されたプログラム」で動けば十分だと思っていました。ある日、本番環境を3日間スキャンして初めて気づいたのです。イメージ内の脆弱性はいくつかのカテゴリに分かれ、それぞれが十分に厄介だということに。
OSパッケージ脆弱性 — 最も一般的です。AlpineやUbuntuベースイメージ内のOpenSSL、glibc、curlなどのシステムライブラリは、定期的に新たな脆弱性が報告されます。2022年のOpenSSL 3.0.x高危脆弱性は、大量のイメージに影響しました。
アプリ依存関係の脆弱性 — より見えにくいタイプです。PythonプロジェクトがFlaskに依存し、FlaskがWerkzeugに依存し、Werkzeugのあるバージョンにコード実行脆弱性がある——Pythonをアップグレードしたから安全、とは限りません。依存ツリーに潜む脆弱性に気づかないことがあります。Log4jが典型例で、Log4jを使っていること自体に気づいていないJavaプロジェクトが数多くありました。
設定ミスと機密情報漏洩 — 手軽さ優先でDockerfileにDBパスワードを直書きしたり、.git ディレクトリを削除せずにパッケージ化したり、rootユーザーでコンテナを実行したり。権限を自宅の鍵より気軽に渡しているようなものです。
サプライチェーンリスク — 最も防ぎにくいタイプです。Docker Hubから一見正常なイメージをプルしても、マイニングプログラムやバックドアが仕込まれている可能性があります。2018年の研究では、Docker Hubに標的型の悪意あるイメージが存在することが判明しています。
なぜDocker Hubのイメージを盲信してはいけないのか?
76%という数字を初めて見たとき、本当に驚きました。
「このデータ、古くない?」と思うかもしれません。しかし問題は、それらの古いイメージが今も使われていることです。多くの「公式イメージ」もリアルタイム更新ではありません。python:3.9 タグは半年前にビルドされたものかもしれず、中のシステムパッケージは既に古い状態です。
さらに厄介なのは、Docker Hubのイメージ品質がばらついていることです。個人開発者が保守し半年更新されていないものもあれば、「公式」と表示されていても実際はある企業がアップロードしただけのものもあります。統一されたセキュリティ監査基準がなく、運用は運次第です。
以前、同僚が手軽さ優先でDocker HubからNode.js環境付きイメージをプルしたところ、後からスキャンしたら30以上の高危脆弱性が見つかりました。作者に問い合わせると「2年間メンテしていない、自分で直して」と返ってきた——苦笑いするしかありませんでした。
今の私の原則は:Docker Hubからプルしたイメージは、公式タグであっても必ずスキャンしてから使うことです。
主要イメージスキャンツールの比較と選び方
Trivy:最も推奨するオープンソースツール
Dockerイメージのセキュリティスキャンツールは選択肢が多いですが、私はほぼTrivyだけを使っています。他のツールが劣っているわけではなく、Trivyが「使いやすさ」と「機能性」のバランスを非常によく取っているからです。
初めてTrivyを使ったとき、その速度に驚きました。数百MBのイメージでも初回スキャンは約10秒、以降は数秒で完了します。ローカルDBのメンテナンスも追加サービスの設定も不要で、インストールすればすぐ使えます。
Trivyが検出できるものは、想像以上に多いです。
- 脆弱性検出:Alpine、Ubuntu、Debian、CentOSなど各種OSパッケージと、npm、pip、Maven、Go Modulesなどのアプリ依存関係
- 設定ミススキャン:Dockerfile、Kubernetes設定ファイルのセキュリティ問題
- Secret漏洩検出:イメージに誤ってパッケージされたキーやパスワードなどの機密情報
- ライセンスコンプライアンス:依存パッケージのオープンソースライセンス確認
特に満足しているのは、GitHub ActionsやHarborなど主要プラットフォームに既に統合されている点です。自分で統合を一から組む必要がなく、公式が道を整えてくれています。
他のツールを簡単に紹介
Snyk — 商用製品で機能は非常に充実しています。IDEに直接統合でき、コーディング中にリアルタイムで依存関係の脆弱性を警告します。修正PRの自動生成にも対応。ただし無料版は制限が多く、本格的に使うには費用がかかります。予算のある大企業向けです。
Clair — Quayイメージレジストリのスキャンエンジンで、オープンソースかつ深くカスタマイズ可能です。各LinuxディストリビューションのセキュリティチームからCVE情報を取得し、脅威検出に特化しています。専任セキュリティエンジニアがいて高度にカスタマイズしたいチームには良い選択ですが、一般の開発チームには学習コストが高めです。
Anchore — エンタープライズ向けソリューションで、ポリシー管理に対応。「CRITICALレベルの脆弱性があればデプロイを阻止」といったルールを設定できます。金融・医療などコンプライアンス要件が厳しいシーン向けですが、設定とメンテナンスの負担も大きいです。
Docker Scout / Hardened Images (DHI) — Docker公式が2025年5月に発表した新製品で、CVEほぼゼロ、イメージサイズ95%削減を謳っています。distrolessランタイムを使った技術方案で、確かに有望です。ただし発売間もなくエコシステムはまだ構築中なので、注目は続けたいところです。
選び方の提案
中小チームや個人開発者なら、Trivyをそのまま使うのが最善です。無料、使いやすく、機能も十分。インストール1コマンド、スキャン1コマンド、10分で始められます。
大企業でセキュリティコンプライアンス要件が高く予算もあるなら、Snykを検討してください。脆弱性DBの更新が早く、サポートも充実し、各種開発ツールと深く統合できます。
専任セキュリティエンジニアがいて、深くカスタマイズ可能なオープンソース方案が欲しいなら、Clairは研究する価値があります。ただし設定に時間をかける覚悟が必要です。
極限のセキュリティと最小イメージを追求するなら、Docker Hardened Imagesの動向に注目してください。将来の標準になるかもしれません。
私の実践は:日常開発とCI/CDではTrivyで素早くフィードバック、本番デプロイ前にHarbor統合のClairで二次スキャン——二重の安全策です。
Trivy実践チュートリアル
インストールと基本操作
Trivyのインストールは、初めて使ったとき「本当にこれだけ?」と思うほど簡単です。macOSなら:
brew install trivy
Linuxユーザーはバイナリを直接ダウンロードするか、パッケージマネージャーでインストールできます。GitHub releasesページに詳しい手順があります:https://github.com/aquasecurity/trivy/releases
インストール後、すぐにイメージをスキャンしてみましょう:
# Docker Hubのイメージをスキャン
trivy image python:3.9
# ローカルでビルドしたイメージをスキャン
trivy image myapp:latest
# tarファイルとして保存したイメージをスキャン
trivy image --input ruby-3.1.tar
初回実行時、Trivyは脆弱性データベース(trivy-db)を自動ダウンロードします。約数十MBで、少し待つ必要があります。ダウンロード完了後、スキャン速度は一気に上がります。
スキャン結果は脆弱性の重要度別に表示されます。
- CRITICAL(重大):即時修正必須、システム完全制御の可能性
- HIGH(高危):早急に修正、攻撃悪用の可能性
- MEDIUM(中危):修正推奨、一定のセキュリティリスク
- LOW(低危):優先度低、後日対応可
各脆弱性にはCVE番号、影響を受けるパッケージ名、現在のバージョン、修正バージョン(あれば)が表示されます。
高度なスキャンオプション:Trivyをもっと使いやすく
実務では、すべての脆弱性を見る必要はありません。特に「修正不能」な低危脆弱性は、見ているだけでストレスです。ここで高度なオプションが活きます。
高危・重大脆弱性のみ表示:
trivy image --severity HIGH,CRITICAL nginx:latest
CI/CDではよく使います。すべての脆弱性を修正するのは現実的ではないので、まず高危なものを解決するのが先決です。
未修正の脆弱性を無視:
trivy image --ignore-unfixed redis:latest
公式パッチがまだ出ていない脆弱性を知っても対応しようがありません。このオプションで、修正可能な脆弱性に集中できます。
重大脆弱性検出時に非ゼロ終了コード:
trivy image --exit-code 1 --severity CRITICAL myapp:latest
CI/CDパイプラインで非常に有用です。重大脆弱性が見つかると非ゼロ終了コードを返し、ビルドが失敗します。イメージにセキュリティゲートが追加され、脆弱性のあるイメージは本番環境に入れません。
JSON形式で出力、他ツールとの統合に便利:
trivy image -f json -o results.json myapp:latest
スキャン結果をセキュリティプラットフォームに取り込んだり、さらなる自動処理を行うなら、JSON形式が便利です。
オフライン環境でのスキャン:
trivy image --skip-db-update myapp:latest
社内ネットワークで外部アクセスできない場合、インターネット接続可能なマシンで脆弱性DBをダウンロードし、社内ネットワークのマシンにコピーします。このオプションで更新試行をスキップできます。
スキャン結果の読み方:何に注目すべきか
初めて本番イメージをスキャンしたとき、100以上の脆弱性が画面いっぱいに表示され、心が折れかけました。「いつまで修正すればいいんだ」と。
経験を積むと、結果の読み方にはコツがあることがわかりました。
修正バージョンがある脆弱性を優先。Fixed Version列が「none」または空欄なら、現時点でパッチがありません。まず修正可能なものから対応しましょう。
CRITICALとHIGHに集中。LOWやMEDIUMは、インターネット公開サービスでなければ後回しにできます。CRITICALは即修正が必要で、公開exploitコードがあり直接悪用可能なことが多いです。
CVE詳細を確認。特定の脆弱性のリスクが判断しにくい場合、https://cve.mitre.org/ でCVE番号を検索し、問題内容・影響範囲・悪用難易度を確認してください。見た目ほど深刻でない場合もあります。
Trivyの仕組みを理解。Trivyは脆弱性データベース(各種ソフトウェアパッケージの既知脆弱性を記録したJSONファイル)をダウンロードし、イメージ内のパッケージリスト(パッケージマネージャーのDBを解析して取得)と照合します。パッケージ名とバージョンが既知脆弱性と一致すれば警告します。
そのため誤報もあります。脆弱性のあるパッケージがイメージに含まれていても、実際のコードが問題の関数を呼んでいなければ、Trivyは警告します。実際のコードと照合してリスクを判断する必要があります。
脆弱性修正の体系的アプローチ
適切なベースイメージの選択から
脆弱性修正は、多くの場合「直す」より「替える」方が効率的です。数十のシステムパッケージ脆弱性が出たら、一つずつアップグレードするより、より安全なベースイメージに替える方が早いです。
以前のチームはAlpine Linuxをベースイメージに使っていました。5.87MBと非常に小さいからです。しかしAlpineにも問題があります。musl libcを使いglibcと互換性問題が出ることもあり、小さくても脆弱性は避けられません。
今は Distroless イメージを好んで使います。3.06MBとAlpineより小さく、極限まで削ぎ落とされています。Shellもパッケージマネージャーも不要なツールもありません。
なぜより安全か? シンプルです。ハッカーがアプリの脆弱性を見つけても、コンテナ内でshellコマンドを実行できません。shell自体が存在しないからです。攻撃面が最小化されます。
GoogleのDistrolessイメージには各言語版があります:
# Pythonアプリ
FROM gcr.io/distroless/python3
# Node.jsアプリ
FROM gcr.io/distroless/nodejs
# Goアプリ(静的コンパイルならstatic版も可)
FROM gcr.io/distroless/static
Distrolessの不便な点は、デバッグが難しいことです。ls や cat すらありません。私のやり方は、開発環境では通常イメージでデバッグしやすく、本番環境ではDistrolessに切り替えることです。
2025年にDocker公式が発表した Hardened Images (DHI) はさらに攻撃的で、CVEほぼゼロ、イメージサイズ95%削減を謳っています。エコシステムはまだ成熟していませんが、次世代の標準方案として注目に値します。
依存パッケージのアップグレードによる脆弱性修正
ベースイメージを替えただけでは不十分で、アプリ層の依存関係も管理が必要です。実用的なテクニックをいくつか紹介します。
方法1:ベースイメージのバージョン更新
# latestは曖昧すぎる
FROM python:3.9
# 具体的なマイナーバージョンを指定し、定期的に更新
FROM python:3.11.7
latest タグは手軽ですが悪い習慣です。数ヶ月更新されず、中のシステムパッケージが古いままのことがあります。具体バージョンを指定し、毎月マイナーバージョン更新を確認しましょう。
方法2:Dockerfileでシステムパッケージをアップグレード
FROM ubuntu:22.04
# ビルド時にすべてのシステムパッケージを更新
RUN apt-get update && \
apt-get upgrade -y && \
apt-get clean && \
rm -rf /var/lib/apt/lists/*
コツ:update、upgrade、cleanを1つのRUN命令にまとめてレイヤー数を削減。最後にaptキャッシュを削除すれば、さらに数十MB節約できます。
方法3:アプリ依存関係のバージョン更新
最も一般的なシーンです。TrivyがFlaskの脆弱性を報告したら、requirements.txt のバージョンを変更します:
# 修正前
Flask==2.0.1
# 修正後(2.3.0で脆弱性が修正されたと仮定)
Flask==2.3.0
ただし注意:Flaskを直接アップグレードすると互換性問題が出ることがあります。テスト環境で確認してから本番へ進みましょう。
より安全な方法はパッチバージョンのみアップグレードすることです。Flask 2.0.1に脆弱性があれば、いきなり2.3.0に上げず、2.0.x最新版を試してください。
見落としがちなベストプラクティス
具体的な脆弱性修正以外にも、イメージを長期的に安全に保つ習慣があります。
マルチステージビルド:
# ビルドステージ
FROM golang:1.21 AS builder
WORKDIR /app
COPY . .
RUN go build -o myapp
# 実行ステージ(コンパイル済みバイナリのみ)
FROM gcr.io/distroless/static
COPY --from=builder /app/myapp /
CMD ["/myapp"]
最終イメージにGoコンパイラ、ソースコード、中間ファイルが含まれません。すっきりして攻撃面が大幅に縮小します。
rootユーザーでの実行を避ける:
RUN useradd -m myuser
USER myuser
多くのイメージはデフォルトでrootでアプリを実行します。これは危険です。ハッカーがアプリを突破すればroot権限を取得し、自由に操作できます。一般ユーザーを作成し、そのユーザーで実行する方が安全です。
定期的なイメージ再ビルド:
見落とされがちです。コードは半年動かしていなくても、ベースイメージのシステムパッケージはパッチが更新され続けています。月1回以上は再ビルドし、最新のセキュリティパッチを適用しましょう。
私たちのチームは毎週日曜にすべての本番イメージを自動再ビルドし、スキャンします。問題があれば月曜に発見・修正できます。
.dockerignoreで機密ファイルのパッケージ化を防止:
.git
.env
*.log
secrets/
.env ファイルを誤ってイメージに含めると、DBパスワードが漏洩します。.dockerignore は .gitignore と同様に、機密ファイルのパッケージ化を防ぎます。
latestタグは絶対に使わない:
再度強調します。FROM python:latest は時限爆弾です。どのバージョンがプルされるか、脆弱性があるかわかりません。FROM python:3.11.7-slim のように明確なタグを使い、予測可能・追跡可能にしましょう。
自動化セキュリティスキャンのCI/CDフロー構築
GitHub Actions統合:5分で完了
手動スキャンは面倒で、忘れがちです。最も確実な方法は、CI/CDにスキャンを組み込み、コードコミットやイメージビルドのたびに自動チェックすることです。
GitHub ActionsへのTrivy統合は驚くほど簡単です。リポジトリに .github/workflows/docker-scan.yml を作成します:
name: Docker Security Scan
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main ]
jobs:
scan:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Build Docker image
run: docker build -t myapp:${{ github.sha }} .
- name: Run Trivy vulnerability scanner
uses: aquasecurity/trivy-action@master
with:
image-ref: 'myapp:${{ github.sha }}'
format: 'table'
severity: 'CRITICAL,HIGH'
exit-code: '1' # 重大脆弱性検出時にビルド失敗
- name: Upload scan results
if: always()
uses: github/codeql-action/upload-sarif@v2
with:
sarif_file: 'trivy-results.sarif'
この設定の動作:
- mainまたはdevelopブランチへのpush、またはPR時に自動トリガー
- Dockerイメージをビルド(commit SHAをタグに使用し、毎回ユニークに)
- TrivyでCRITICALとHIGHレベルの脆弱性のみチェック
- 重大脆弱性があれば
exit-code: '1'でworkflowが失敗し、マージを阻止 - スキャン結果をGitHub Securityタブにアップロード
初回設定後、コードをpushしてみてください。GitHub Actionsタブでスキャン結果が確認できます。脆弱性があればビルドが赤くなり、mainブランチにマージされません。自動セキュリティゲートが追加された状態です。
GitLab CI/CD統合:同様に簡単
GitLabを使っている場合も、統合方法はほぼ同じです。リポジトリルートに .gitlab-ci.yml を作成または修正します:
stages:
- build
- test
- security
build_image:
stage: build
image: docker:latest
services:
- docker:dind
script:
- docker build -t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA .
- docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
security_scan:
stage: security
image: aquasec/trivy:latest
script:
- trivy image --exit-code 1 --severity HIGH,CRITICAL $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
allow_failure: false
dependencies:
- build_image
allow_failure: false は、セキュリティスキャン失敗でパイプライン全体を失敗させる設定です。厳しくしたくない場合は allow_failure: true に変更できますが、脆弱性があっても警告のみでデプロイは止まりません。
本番環境のパイプラインは必ずfalseに設定することをお勧めします。開発環境は緩めでも、本番環境では妥協できません。
Harborイメージレジストリによる二重の安全策
Harborをプライベートイメージレジストリとして使っている企業(大企業に多い)なら、さらに楽です。Harborはv1.2からTrivyとClairを統合し、プッシュされたイメージを自動スキャンできます。
Harborのプロジェクト設定でスキャン戦略を設定できます。
- イメージプッシュ時に自動スキャン
- 毎日すべてのイメージを定期スキャン(新たな脆弱性公開に対応)
- 脆弱性閾値設定:CRITICAL脆弱性があるイメージのプルを禁止
私たちのチームの戦略:
- 開発環境でイメージをHarborにpushすると、Trivyスキャンが自動トリガー
- HIGH以上の脆弱性があれば、Harborが「脆弱性あり」タグを付与
- 本番K8sクラスタでadmission webhookを設定し、脆弱性タグ付きイメージのデプロイを拒否
CI/CDを迂回して直接デプロイしようとしても、Harborが最後の関所で阻止します。
完全なセキュリティスキャンフローの提案
これまでの内容をまとめると、完全なイメージセキュリティフローは次のようになります。
開発段階:
- IDEにSnykまたはTrivyプラグインを統合し、コーディング中に依存関係の脆弱性を確認
- ローカルでイメージビルド後、
trivy imageで手動チェック
ビルド段階:
- GitHub Actions / GitLab CIで新規ビルドイメージを自動スキャン
- CRITICAL脆弱性検出時に即座に失敗しマージを阻止
- スキャン結果をSecurity Dashboardにアップロードし、チームでトレンドを確認
レジストリ段階:
- Harborなどで二次スキャン(CI/CDの見落としや新たな脆弱性公開に対応)
- 脆弱性ポリシーで高危イメージのプルを阻止
実行時:
- 本番環境の実行中イメージを定期スキャン(週1回)
- 新たな脆弱性検出時にアラートを発報し、修正をスケジュール
定期レビュー:
- 月次で脆弱性修正状況をレビュー
- スキャン戦略と脆弱性閾値を更新
一見複雑に見えますが、一度設定すれば自動化されます。私たちのチームは設定から安定運用まで約1スプリント(2週間)かかりましたが、長期的なセキュリティ保障が得られました。
このフローを設定してから、夜も安心して眠れるようになりました。突然大きな脆弱性が報告され、自分のイメージが既に影響を受けている——そんな心配が減りました。
結論
記事冒頭のLog4Shellの話に戻りましょう。当時このイメージセキュリティスキャンフローがあれば、脆弱性はCI/CD段階で発見され、本番環境には入りませんでした。あの緊迫した深夜も、上司の魂の問いも、起こらなかったはずです。
Dockerイメージセキュリティは難しい技術ではありません。核心は3つです。
- Trivyなどのツールでイメージ脆弱性をスキャン(インストール簡単、高速、十分な機能)
- 体系的な脆弱性修正(安全なベースイメージ選択、依存関係アップグレード、ベストプラクティス遵守)
- CI/CDでの自動スキャン(GitHub Actions / GitLab CI、5分で統合)
76%という数字は最初は驚きましたが、多くのチームがまだ十分に取り組んでいないことの表れでもあります。今から始めれば、少なくとも76%のチームより良い状態にできます。それ自体が達成感ではないでしょうか。
提案:すべての脆弱性を一度に修正しようとしないでください。現実的ではありません。最も簡単なところから始めましょう。
- 今日中に:Trivyをインストールし、プロジェクトのDockerイメージをスキャンして脆弱性数を確認
- 今週中に:CRITICALレベルの脆弱性を修正(ベースイメージバージョン更新だけで済むことも多い)
- 次のスプリント:CI/CDにTrivyを統合し、重大脆弱性検出時にデプロイを阻止
- 今月中:イメージセキュリティ基準を策定し、定期レビューと更新の仕組みを作る
コンテナセキュリティは継続的なプロセスであり、一度きりではありません。フローを確立すれば、その後のメンテナンスは本当に大変ではありません。私たちのチームは毎週自動スキャンし、問題があれば週次ミーティングで30分程度で処理しています。
最後にもう一度:本番環境のイメージは必ずスキャンしてください。Docker Hubからプルしたイメージも必ずスキャンしてください。事故が起きてから後悔するのでは遅いのです。
Dockerセキュリティの実践経験や失敗談があれば、コメント欄でぜひ共有してください。一緒に進歩し、コンテナ化アプリケーションをより安全にしましょう。
Dockerイメージのセキュリティスキャンと修正の完全フロー
TrivyのインストールからCI/CD統合まで、スキャン・修正・自動化の全ステップを解説
⏱️ 目安時間: 2 時間
- 1
ステップ1: Trivyスキャンツールのインストール
Trivyスキャンツールのインストール:
• macOSユーザー:brew install trivy
• Linuxユーザー:GitHub releasesページからバイナリをダウンロードするか、パッケージマネージャーでインストール
• ダウンロード先:https://github.com/aquasecurity/trivy/releases
• 初回実行時に脆弱性データベース(trivy-db、約数十MB)を自動ダウンロード - 2
ステップ2: イメージ脆弱性の基本スキャン
イメージ脆弱性の基本スキャン:
• Docker Hubイメージをスキャン:trivy image python:3.9
• ローカルイメージをスキャン:trivy image myapp:latest
• tarファイルをスキャン:trivy image --input ruby-3.1.tar
スキャン結果は重要度別に分類:
• CRITICAL(即時修正必須)
• HIGH(早急に修正)
• MEDIUM(修正推奨)
• LOW(後日対応可) - 3
ステップ3: 高度なスキャンオプションの設定
高度なスキャンオプションの設定:
• 高危・重大脆弱性のみ表示:trivy image --severity HIGH,CRITICAL nginx:latest
• 未修正の脆弱性を無視:trivy image --ignore-unfixed redis:latest
• 重大脆弱性検出時に非ゼロ終了コード(CI/CD用):
trivy image --exit-code 1 --severity CRITICAL myapp:latest
• JSON形式で出力:trivy image -f json -o results.json myapp:latest - 4
ステップ4: 脆弱性の体系的修正
脆弱性の体系的修正:
安全なベースイメージを選ぶ:
• Distrolessイメージ(gcr.io/distroless/python3/nodejs/static、3.06MBのみ、Shell・パッケージマネージャーなし)
• または2025年のDocker Hardened Images(CVEほぼゼロ)
ベースイメージのバージョン更新:
• 具体バージョン番号を使用(python:3.11.7)、latestは使わない
Dockerfileでシステムパッケージをアップグレード:
• RUN apt-get update && apt-get upgrade -y && apt-get clean && rm -rf /var/lib/apt/lists/*
アプリ依存関係の更新:
• requirements.txtなどの依存ファイルを修正し、パッチバージョンを優先的にアップグレード - 5
ステップ5: GitHub Actionsへの自動スキャン統合
.github/workflows/docker-scan.ymlファイルを作成し、以下を設定:
• on push/pull_requestでトリガー
• Dockerイメージをビルド
• aquasecurity/trivy-actionでスキャン
• severity: 'CRITICAL,HIGH'とexit-code: '1'を設定(重大脆弱性検出時にビルド失敗)
• スキャン結果をGitHub Securityタブにアップロード - 6
ステップ6: GitLab CI/CD統合
.gitlab-ci.ymlを作成または修正し、以下を設定:
• stagesにbuild、test、securityを含める
• securityステージでaquasec/trivy:latestイメージを使用
• trivy image --exit-code 1 --severity HIGH,CRITICALコマンドを実行
• allow_failure: falseを設定(セキュリティスキャン失敗でパイプライン全体を失敗させる) - 7
ステップ7: 完全なセキュリティスキャンフローの構築
開発段階:
• IDEにSnyk/Trivyプラグインを統合
• ローカルでイメージビルド後に手動スキャン
ビルド段階:
• CI/CDで自動スキャン
• CRITICAL脆弱性検出時に即座に失敗しマージを阻止
• スキャン結果をSecurity Dashboardにアップロード
レジストリ段階:
• Harborなどのイメージレジストリで二次スキャン
• 脆弱性ポリシーで高危イメージのプルを阻止
実行時:
• 本番環境イメージを定期スキャン(週1回)
• 新たな脆弱性検出時にアラートを発報
定期レビュー:
• 月次で脆弱性修正状況をレビュー
• スキャン戦略と脆弱性閾値を更新
FAQ
Dockerイメージにはどのようなセキュリティ問題がありますか?
1) OSパッケージ脆弱性:
• Alpine、Ubuntuベースイメージ内のOpenSSL、glibc、curlなどのシステムライブラリ
• 例:2022年のOpenSSL 3.0.x高危脆弱性
2) アプリ依存関係の脆弱性:
• 依存ツリーに潜む脆弱性、Log4j事例のように
• 多くのJavaプロジェクトはLog4jを使っていること自体に気づいていない
3) 設定ミスと機密情報漏洩:
• DockerfileにDBパスワードを直書き
• .gitディレクトリの削除忘れ
• rootユーザーでコンテナを実行
4) サプライチェーンリスク:
• Docker Hub上の悪意あるイメージ
• 2018年の研究で、標的型の悪意あるイメージが存在することが判明
なぜDocker Hubのイメージを盲信してはいけないのですか?76%という数字は正確ですか?
データはやや古いですが、それらの古いイメージは今も使われています。多くの「公式イメージ」もリアルタイム更新ではなく、python:3.9タグは半年前にビルドされたものかもしれず、システムパッケージは既に古い状態です。
Docker Hubのイメージ品質はばらつきがあります:
• 個人開発者が保守し、半年更新されていないものもある
• 「公式」と表示されていても、実際はある企業がアップロードしただけのもの
• 統一されたセキュリティ監査基準がない
原則:Docker Hubからプルしたイメージは、公式タグであっても必ずスキャンしてから使うこと。
Trivyと他のスキャンツール(Snyk、Clair、Anchore)の違いは?どれを選ぶべきですか?
• 最も推奨されるオープンソースツール、無料で使いやすく機能十分
• インストールが簡単(brew install trivy)
• スキャンが高速(初回10秒、以降は数秒)
• 脆弱性検出、設定ミススキャン、Secret漏洩検出、ライセンスコンプライアンスに対応
• GitHub Actions、Harborに統合済み
• 10分で使い始められ、中小チームや個人開発者向け
Snyk:
• 商用製品で機能が充実
• IDEへの統合でリアルタイム警告
• 修正PRの自動生成に対応
• 無料版は制限が多く、予算のある大企業向け
Clair:
• Quayイメージレジストリのスキャンエンジン、オープンソースで深くカスタマイズ可能
• 設定が複雑で学習コストが高い
• 専任セキュリティエンジニアがいるチーム向け
Anchore:
• エンタープライズ向けソリューション、ポリシー管理に対応
• コンプライアンス要件が厳しい金融・医療業界向け
推奨:中小チームはTrivy、大企業はSnyk、セキュリティエンジニアがいるチームはClairを検討。
Trivyのスキャン結果の読み方は?何に注目すべきですか?
• CRITICAL(即時修正必須、システム完全制御の可能性)
• HIGH(早急に修正、攻撃悪用の可能性)
• MEDIUM(修正推奨、一定のセキュリティリスク)
• LOW(優先度低、後日対応可)
結果の読み方:
1) 修正バージョンがある脆弱性を優先(Fixed Versionがnoneまたは空欄なら現時点でパッチなし、修正可能なものから対応)
2) CRITICALとHIGHに集中(CRITICALは即修正、公開exploitコードがあり直接悪用可能なことが多い)
3) CVE詳細を確認(https://cve.mitre.org/でCVE番号を検索し、問題内容・影響範囲・悪用難易度を確認)
4) Trivyの仕組みを理解(脆弱性DBのJSONファイルをダウンロードし、イメージ内のパッケージリストと照合。パッケージ名とバージョンが一致すれば警告。誤報もあり、実際のコードと照合してリスク判断が必要)
Dockerイメージの脆弱性を体系的に修正するには?
1) 安全なベースイメージを選ぶ:
• Distrolessイメージは3.06MBでAlpineより小さく、Shell・パッケージマネージャーなしで攻撃面が最小
• GoogleのDistrolessにpython3/nodejs/static版がある
• 2025年のDocker Hardened ImagesはCVEほぼゼロ、イメージサイズ95%削減を謳う
2) ベースイメージのバージョン更新:
• 具体バージョン番号python:3.11.7を使用、latestは使わない
• latestは数ヶ月更新されずシステムパッケージが古いまま
• 毎月マイナーバージョン更新を確認
3) Dockerfileでシステムパッケージをアップグレード:
• RUN apt-get update && apt-get upgrade -y && apt-get clean && rm -rf /var/lib/apt/lists/*
• update/upgrade/cleanを1つのRUN命令にまとめてレイヤー数を削減
4) アプリ依存関係の更新:
• requirements.txtなどを修正
• パッチバージョンを優先(Flask 2.0.1に脆弱性があれば2.0.x最新版を試し、いきなり2.3.0に上げない)
• テスト環境で確認してから本番へ
ベストプラクティス:マルチステージビルド、rootユーザーでの実行禁止、定期的なイメージ再ビルド(月1回以上)、.dockerignoreで機密ファイル除外、latestタグは絶対に使わない。
CI/CDに自動セキュリティスキャンを統合するには?
• .github/workflows/docker-scan.ymlを作成
• on push/pull_requestでトリガー
• Dockerイメージをビルドし、aquasecurity/trivy-actionでスキャン
• severity: 'CRITICAL,HIGH'とexit-code: '1'を設定(重大脆弱性検出時にビルド失敗)
• スキャン結果をGitHub Securityタブにアップロード
GitLab CI統合:
• .gitlab-ci.ymlを作成または修正
• stagesにbuild/test/securityを設定
• securityステージでaquasec/trivy:latestイメージを使用
• trivy image --exit-code 1 --severity HIGH,CRITICALを実行
• allow_failure: falseを設定(セキュリティスキャン失敗でパイプライン全体を失敗、本番環境では必須)
Harborイメージレジストリ:
• v1.2からTrivyとClairを統合
• イメージプッシュ時の自動スキャン、毎日の定期スキャンを設定可能
• 脆弱性閾値を設定(CRITICAL脆弱性があるイメージのプルを禁止)
• 開発環境でpush時に自動スキャン、HIGH以上の脆弱性で「脆弱性あり」タグ付与
• 本番K8sクラスタでadmission webhookを設定し、脆弱性タグ付きイメージのデプロイを拒否
完全なDockerイメージセキュリティスキャンフローはどうあるべきですか?
開発段階:
• IDEにSnykまたはTrivyプラグインを統合し、コーディング中に依存関係の脆弱性を確認
• ローカルでイメージビルド後にtrivy imageで手動チェック
ビルド段階:
• GitHub Actions/GitLab CIで新規ビルドイメージを自動スキャン
• CRITICAL脆弱性検出時に即座に失敗しマージを阻止
• スキャン結果をSecurity Dashboardにアップロードし、チームでトレンドを確認
レジストリ段階:
• Harborなどで二次スキャン
• CI/CDの見落としや新たな脆弱性公開に対応
• 脆弱性ポリシーで高危イメージのプルを阻止
実行時:
• 本番環境の実行中イメージを定期スキャン(週1回)
• 新たな脆弱性検出時にアラートを発報し修正をスケジュール
定期レビュー:
• 月次で脆弱性修正状況をレビュー
• スキャン戦略と脆弱性閾値を更新
このフローは一度設定すれば自動化され、チームは設定から安定運用まで約1スプリント(2週間)かかりますが、長期的なセキュリティ保障が得られます。
9分で読めます · 公開日: 2025年12月18日 · 更新日: 2026年6月8日
Docker 実践ガイド
検索からこのページに来た場合は、前後の記事もあわせて読むと同じテーマの理解がかなり早く深まります。
前の記事
Docker で Nginx 構築完全ガイド:設定ファイルのマウント、HTTPS 設定、リバースプロキシ実践
Docker で Nginx を動かすための設定ファイルマウント、HTTPS 証明書、リバースプロキシの完全チュートリアル。設定が反映されない問題やコンテナ間通信のトラブルを解決し、Let's Encrypt 自動更新と本番環境のベストプラクティスも解説。
第 28 / 37 記事
次の記事
Docker セキュリティ設定:root 実行を避ける本番向け完全ガイド
Docker コンテナをデフォルトの root で動かすのは深刻なセキュリティリスクです。コンテナ脱出の仕組みから、Dockerfile の USER 命令、--user パラメータ、Capabilities による権限管理、AppArmor 設定まで、本番レベルのセキュリティ対策を解説します。
第 30 / 37 記事
関連記事
Dockerfile入門:ゼロから最初の Docker イメージを作る(実例付き)
Dockerfile入門:ゼロから最初の Docker イメージを作る(実例付き)
Docker vs 仮想マシン:5分で理解する性能差とシーン別選び方ガイド
Docker vs 仮想マシン:5分で理解する性能差とシーン別選び方ガイド
Docker インストールの落とし穴ガイド 2025:permission denied から正常起動までの完全解決策
コメント
GitHubアカウントでログインしてコメントできます