Le scan de conteneurs est l'analyse automatisée d'une image Docker ou OCI pour détecter les vulnérabilités connues (CVE), les secrets accidentellement embarqués, les mauvaises configurations et les écarts par rapport aux benchmarks de sécurité (CIS Docker, CIS Kubernetes). Il s'exécute à quatre moments complémentaires du cycle de vie : pendant le build CI/CD, lors du push en registry, en scan continu du registry, et au runtime via admission controller Kubernetes. Les outils de référence en 2026 sont Trivy (Aqua Security, open source, standard de facto), Grype (Anchore), Docker Scout (natif Docker), Snyk Container (commercial) et Clair (open source, historique). Sans scan conteneur, une application parfaitement sécurisée côté code peut hériter de 50 à 200 CVE critiques via son image de base et ses paquets OS. Cet article détaille pourquoi c'est indispensable, quels outils choisir, comment intégrer dans le pipeline, et les bonnes pratiques pour minimiser la surface d'attaque.
Pourquoi scanner les conteneurs
Une image conteneur = beaucoup plus que du code applicatif
Une image Docker typique combine plusieurs couches qui cumulent chacune leurs vulnérabilités potentielles.
| Couche | Contenu | Risques |
|---|---|---|
| Base OS | Alpine, Ubuntu, Debian, RHEL UBI | CVE glibc, openssl, coreutils, shells |
| Runtime langage | Python, Node.js, JVM, Go runtime | CVE interpréteur ou runtime |
| Dépendances système | Paquets installés (apt, apk, dnf) | CVE paquets distro, backdoors supply chain |
| Bibliothèques dynamiques | libcurl, libxml2, libxslt, openssh | CVE Heartbleed-like, buffer overflows |
| Dépendances applicatives | package.json, requirements.txt | Déjà couvertes par SCA mais aussi dans l'image |
| Outils embarqués | curl, wget, bash, coreutils, debug | Amplifient l'exploitation si compromise |
| Configuration | Utilisateur, ports, ENTRYPOINT | Privilèges root, ports exposés inutiles |
| Secrets | Clés API, tokens, mots de passe | Fuite via layers, registre public |
3 incidents récents qui illustrent le risque
XZ Utils backdoor - CVE-2024-3094 (mars 2024)
Backdoor SSH dans la bibliothèque xz installée dans de nombreuses
images de base Debian / Fedora / Ubuntu rolling.
Des scans d'images publics ont révélé la présence dans des dizaines
d'images Docker Hub non rebuildées pendant des semaines après disclosure.
OpenSSL CVE-2022-3602 et 3786 (novembre 2022)
Buffer overflow dans le parsing de certificats X.509.
Toutes les images avec OpenSSL 3.0.0 à 3.0.6 vulnérables.
Impact massif pour les équipes qui n'ont pas rebuildé leurs images.
Docker Hub typosquatting et cryptojacking (continu 2020-2025)
Des images populaires typosquattées ou piégées embarquent
des crypto-miners. Une image pullée d'un registre public non vérifié
peut tourner en prod avec un payload silencieux.Chiffres 2025-2026
Selon les rapports Sysdig Cloud Native Security (2024) et Aqua Security (2025) :
- Une image de base non optimisée typique contient 200 à 800 CVE toutes sévérités confondues.
- Une image distroless ou Chainguard correspondante : 0 à 5 CVE.
- 70 % des images publiques sur Docker Hub contiennent au moins une vulnérabilité Critical.
- Temps moyen entre disclosure d'une CVE Critical et son arrivée dans les images rebuildées automatiquement : 6 à 14 jours chez les équipes matures, 40 à 90 jours sans automatisation.
Que scanner exactement
Un scan complet couvre cinq dimensions en 2026.
1. Vulnérabilités logicielles (CVE)
Couvre les paquets OS (via distro databases : Debian Security Tracker, Red Hat OVAL, Alpine secdb) et les dépendances applicatives (via NVD, GitHub Advisory Database, OSV.dev). Produit une liste de CVE avec criticité CVSS, patches disponibles, et chemin d'exploitation.
2. Secrets accidentellement embarqués
Clés API, tokens JWT, mots de passe, clés SSH, tokens cloud dans les layers. Souvent dus à un COPY . . aveugle ou à un build arg exposé. Les outils modernes (Trivy, trufflehog) scannent les layers via des patterns regex et des détections sémantiques.
3. Misconfigurations Dockerfile
Anti-patterns fréquents détectés par les scanners :
USER root en ENTRYPOINT (CIS Docker Benchmark 4.1)
ADD au lieu de COPY (téléchargement non vérifié)
curl | bash dans un RUN (exécution non signée)
Pas de HEALTHCHECK (observabilité limitée)
latest tag sur FROM (pas de pinning)
COPY . . avec .dockerignore absent (fuite fichiers sensibles)
Secrets dans ENV ou ARG (persistés dans layer)
CAP_SYS_ADMIN ou --privileged (privilèges excessifs)
Port 22 exposé (SSH dans conteneur rare nécessaire)
Multiple RUN sans multi-stage (image gonflée inutilement)4. Conformité aux benchmarks
- CIS Docker Benchmark : 117 contrôles sur l'hôte Docker et les images.
- CIS Kubernetes Benchmark : sécurité cluster et workloads.
- NSA/CISA Kubernetes Hardening Guide (v1.2, 2022) : recommandations NSA.
- NIST SP 800-190 : application container security guide.
5. SBOM (Software Bill of Materials)
Listing complet des composants installés dans l'image au format CycloneDX ou SPDX. Obligatoire sous Cyber Resilience Act UE et Executive Order 14028 US pour certains marchés.
Outils de scan 2026 : panorama détaillé
Trivy (Aqua Security, open source)
Standard de facto. Binaire unique, rapide, couvre CVE + secrets + misconfig + SBOM. Intégration CI/CD triviale.
# Scan CVE simple
trivy image --severity HIGH,CRITICAL nginx:1.25-alpine
# Scan complet avec secrets et misconfig
trivy image --scanners vuln,secret,misconfig --format json \
--output report.json registry/app:${GITHUB_SHA}
# Générer un SBOM CycloneDX
trivy image --format cyclonedx --output sbom.json registry/app:${GITHUB_SHA}
# Ignorer les CVE non fixables (pas de patch disponible)
trivy image --ignore-unfixed registry/app:${GITHUB_SHA}
# Exit non zéro si CVE High/Critical - fait planter le CI
trivy image --exit-code 1 --severity HIGH,CRITICAL registry/app:${GITHUB_SHA}Grype (Anchore, open source)
Alternative plus légère, souvent couplée à Syft pour la génération de SBOM en amont.
# Pipeline typique : Syft génère SBOM, Grype scanne le SBOM
syft registry/app:${GITHUB_SHA} -o cyclonedx-json > sbom.json
grype sbom:sbom.json --fail-on criticalDocker Scout (natif Docker)
Intégré à Docker Desktop et au CLI docker. Parfait pour les petites équipes sur Docker Hub.
# Vue rapide
docker scout cves registry/app:latest
# Recommandation d'image de base plus sûre
docker scout recommendations registry/app:latest
# Comparaison entre deux tags
docker scout compare --to registry/app:previous registry/app:latestSnyk Container (commercial freemium)
Excelle sur la suggestion automatique de base image alternative et les fix PR automatiques.
snyk container test registry/app:${GITHUB_SHA}
snyk container test --file=Dockerfile registry/app:${GITHUB_SHA}Clair (open source, historique)
API-driven, souvent intégré dans des registries (Harbor, Quay). Plus complexe à déployer qu'un CLI mais excellent pour le scan continu côté registry.
Comparatif synthétique
| Outil | Type | Licence | Forces | Usage typique |
|---|---|---|---|---|
| Trivy | CLI + API | OSS Apache 2.0 | Tout-en-un, rapide, CI-friendly | Scan CI + local dev |
| Grype | CLI | OSS Apache 2.0 | Léger, couplage Syft SBOM | CI, scripting |
| Docker Scout | CLI + Hub | Freemium | UX simple, Docker natif | Petites équipes, Docker Hub |
| Snyk Container | SaaS + CLI | Commercial freemium | Fix PR auto, base image suggest | Scale-up, enterprise |
| Clair | API | OSS Apache 2.0 | Scan registry continu | Quay, Harbor |
| Anchore Enterprise | SaaS | Commercial | Policy as code avancé | Enterprise régulée |
| Prisma Cloud (Twistlock) | SaaS + agent | Commercial | Suite complète CWPP | Multi-cloud enterprise |
| Aqua Enterprise | SaaS + agent | Commercial | Runtime + compliance | Enterprise régulée |
Intégration CI/CD : exemples concrets
GitHub Actions avec Trivy
name: container-scan
on: [push, pull_request]
permissions:
contents: read
security-events: write # requis pour upload SARIF
id-token: write # requis pour cosign keyless
jobs:
build-and-scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build image
run: docker build -t app:${{ github.sha }} .
- name: Trivy scan - fail on Critical
uses: aquasecurity/trivy-action@master
with:
image-ref: app:${{ github.sha }}
severity: HIGH,CRITICAL
exit-code: 1
ignore-unfixed: true
format: sarif
output: trivy-results.sarif
- name: Upload SARIF to GitHub Security
if: always()
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: trivy-results.sarif
- name: Generate SBOM
run: trivy image --format cyclonedx --output sbom.json app:${{ github.sha }}
- name: Upload SBOM as artifact
uses: actions/upload-artifact@v4
with:
name: sbom
path: sbom.json
- name: Install cosign
uses: sigstore/cosign-installer@v3
- name: Sign image (keyless OIDC)
run: |
docker push ghcr.io/org/app:${{ github.sha }}
cosign sign --yes ghcr.io/org/app:${{ github.sha }}
- name: Attach SBOM attestation
run: |
cosign attest --yes --predicate sbom.json \
--type cyclonedx ghcr.io/org/app:${{ github.sha }}GitLab CI équivalent
stages:
- build
- scan
- sign
container-build:
stage: build
script:
- docker build -t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA .
- docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
container-scan:
stage: scan
image: aquasec/trivy:latest
script:
- trivy image --exit-code 1 --severity HIGH,CRITICAL --ignore-unfixed
$CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
container-sign:
stage: sign
script:
- cosign sign --yes $CI_REGISTRY_IMAGE:$CI_COMMIT_SHAScan continu du registry
Un scan au moment du build ne protège pas contre les CVE qui tombent après la publication. Deux approches complémentaires :
Approche 1 - Scan scheduled via CI
# GitHub Actions cron nightly scan registry
on:
schedule:
- cron: '0 2 * * *' # chaque nuit à 2h
jobs:
rescan-registry-images:
runs-on: ubuntu-latest
strategy:
matrix:
image: [app-api, app-worker, app-frontend]
steps:
- name: Trivy rescan
run: |
trivy image --severity HIGH,CRITICAL \
--format json --output ${{ matrix.image }}.json \
ghcr.io/org/${{ matrix.image }}:latest
# Publier les résultats dans Slack/Jira/Dependency-TrackApproche 2 - Registry avec scan intégré
Les registres modernes intègrent nativement un scanner :
- Harbor (CNCF) : Trivy intégré par défaut, Clair disponible.
- Quay (Red Hat) : Clair intégré.
- GitHub Container Registry : Docker Scout intégré.
- Docker Hub : Docker Scout sur les plans payants.
- AWS ECR : Enhanced Scanning avec Clair et Snyk.
- GCP Artifact Registry : Container Analysis (Grafeas).
- Azure Container Registry : Microsoft Defender for Containers.
Admission controllers Kubernetes : la dernière ligne
Un scan CI rigoureux ne suffit pas si un développeur peut déployer une image non vérifiée en cluster. Les admission controllers bloquent les pods qui ne respectent pas la policy.
Kyverno - verify image signature
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: verify-image-signatures
spec:
validationFailureAction: Enforce
rules:
- name: verify-cosign-signature
match:
any:
- resources:
kinds: [Pod]
verifyImages:
- imageReferences:
- "ghcr.io/org/*"
attestors:
- entries:
- keyless:
subject: "https://github.com/org/*/.github/workflows/*"
issuer: "https://token.actions.githubusercontent.com"Cette policy refuse tout pod utilisant une image du registre ghcr.io/org/* qui n'a pas été signée par une GitHub Actions OIDC de l'organisation. Combiné au scan CI, elle garantit qu'une image vulnérable ne peut pas arriver en cluster via shortcut.
OPA Gatekeeper et autres contrôleurs
Alternatives et compléments :
- OPA Gatekeeper : policies Rego, historique, large adoption.
- Kyverno : YAML, plus accessible, fort sur verify-image.
- Ratify + Gatekeeper : verify-image policies Sigstore.
- Falco : runtime detection eBPF, alertes.
Bonnes pratiques pour minimiser la surface
1. Images de base minimales
ubuntu:24.04 : 77 MB, 90+ CVE résiduelles typiques
python:3.12-slim : 45 MB, 20-40 CVE
python:3.12-alpine : 19 MB, 15-30 CVE (mais libc musl)
chainguard/python:latest : 15 MB, 0-3 CVE (sponsored base)
gcr.io/distroless/python3 : 25 MB, 0-5 CVE
scratch + binaire statique Go : 5-15 MB, 0 CVE distro2. Multi-stage builds
# Build stage - gros, outils de build inclus
FROM golang:1.22 AS build
WORKDIR /src
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 go build -o /bin/app ./cmd/app
# Runtime stage - minimal, seul le binaire
FROM gcr.io/distroless/static-debian12:nonroot
COPY --from=build /bin/app /bin/app
USER nonroot
ENTRYPOINT ["/bin/app"]3. Pinning strict et mise à jour automatisée
# MAUVAIS - mutable, non reproductible
FROM python:3.12-slim
# MIEUX - tag minor pinné
FROM python:3.12.6-slim
# IDÉAL - digest SHA256 immuable
FROM python:3.12.6-slim@sha256:1a89c1d4...Automatiser les mises à jour avec Renovate ou Dependabot pour rebuilder les images dès qu'une nouvelle version minor sort.
4. Utilisateur non-root
RUN useradd -u 10001 -m -s /usr/sbin/nologin app
USER 100015. Signature et SBOM systématiques
cosign pour signer, Syft ou Trivy pour SBOM, attestation attachée à l'image via cosign attest. Voir l'exemple GitHub Actions plus haut.
Pièges fréquents à éviter
- Scanner uniquement au build et jamais le registry : les CVE tombent après la publication, il faut rescanner.
- Ignorer les CVE non fixables sans exception tracée :
--ignore-unfixedest utile mais doit faire l'objet d'un suivi. - Scan avec sévérité Medium bloquante en CI : taux de bruit élevé, les équipes finissent par ignorer tout. Commencer par Critical seulement, puis durcir.
- Absence d'admission controller : un pipeline CI parfait ne protège pas d'un
kubectl applydirect avec une image bypass. - SBOM généré mais non stocké : un SBOM sert seulement s'il est archivé et consultable après le déploiement. Dependency-Track (OWASP) est la plateforme de référence.
- Scan seulement de l'application, pas des images sidecars et init containers : inclure tous les conteneurs du pod.
- Absence de signature : Sigstore cosign est gratuit et facile. Ne pas le faire = accepter qu'un attaquant puisse pousser une image piégée avec le même nom.
Points clés à retenir
- Scanner un conteneur = scanner bien plus que le code : OS de base, runtime langage, paquets distro, binaires système, secrets, configurations Dockerfile, benchmarks CIS.
- Quatre moments de scan complémentaires en 2026 : CI (bloquer avant registry), push (scan du registre), scheduled rescan (CVE nouvelles), admission controller K8s (dernière ligne).
- Stack outil minimale 2026 : Trivy (open source, standard) en CI + scan continu registry + Kyverno verify-image en cluster. Ajouter Snyk Container ou Snyk si budget.
- 5 leviers de réduction CVE : images distroless / Chainguard, multi-stage builds, pinning digest SHA256, mises à jour automatisées (Renovate), utilisateur non-root.
- Signer les images avec cosign (keyless OIDC gratuit) et générer un SBOM CycloneDX à chaque build. Obligatoire sous Cyber Resilience Act UE et Executive Order 14028 US.
- Pièges à éviter : scan seulement au build, seuils trop agressifs qui font ignorer, SBOM non archivé, absence d'admission controller, scan limité au conteneur principal.
Pour approfondir la discipline DevSecOps complète et savoir où le scan conteneur s'inscrit, voir la roadmap DevSecOps 2026. Pour comprendre le positionnement SAST vs DAST vs scan conteneur dans un pipeline, lire SAST vs DAST : comparaison complète. Pour la vision cadre général supply chain dont les images font partie, voir software supply chain : définition et enjeux sécurité.





