Image Security and Scanning
LEVEL 0
The Problem
You pull an image from Docker Hub:
docker pull someuser/webapp
How do you know this image is safe?
It might contain:
- Malware or backdoors
- Cryptocurrency miners
- Vulnerable versions of libraries (known CVEs)
- Hardcoded credentials
- Unnecessary tools that increase attack surface
You can’t just trust random images from the internet. Even official images can have vulnerabilities.
LEVEL 1
The Concept — The Food Safety Inspection
The Concept
Imagine a restaurant kitchen.
Before serving food, health inspectors check:
- Source: Where did the ingredients come from? Trusted suppliers?
- Freshness: Are they expired? Spoiled?
- Contamination: Any bacteria? Parasites?
- Storage: Stored properly? Right temperature?
Image scanning is health inspection for Docker images.
You check:
- Source: Trusted registry? Official image?
- Vulnerabilities: Known CVEs in packages?
- Secrets: Hardcoded passwords or keys?
- Configuration: Unnecessary tools? Running as root?
LEVEL 2
The Mechanics — How Images Get Compromised
The Mechanics
Scenario 1: Malicious image
Attacker creates a fake image:
Official: nginx:latest
Fake: ngnix:latest (typo)
Unsuspecting user types:
docker pull ngnix:latest # Typo!
Gets malicious image with backdoor.
Scenario 2: Vulnerable dependencies
Official image, but built 6 months ago. Contains libraries with known CVEs discovered since then.
FROM ubuntu:20.04 # From 2 years ago
RUN apt-get install openssl # Old version with CVE
Scenario 3: Supply chain attack
Maintainer’s account compromised. Attacker pushes malicious update to popular image.
Scenario 4: Secrets in image
Developer accidentally commits:
ENV API_KEY=sk_live_abc123xyz789
Now anyone who pulls the image has your API key.
LEVEL 3
Image Scanning Tools
Trivy (Open Source)
Fast, comprehensive scanner:
# Scan an image
trivy image nginx:latest
# Scan and fail on high/critical
trivy image --severity HIGH,CRITICAL --exit-code 1 nginx:latest
# Scan a Dockerfile
trivy config Dockerfile
Grype (Open Source)
# Install
brew install grype
# Scan image
grype nginx:latest
Snyk
snyk container test nginx:latest
Docker Scout (Docker built-in)
docker scout cves nginx:latest
docker scout recommendations nginx:latest
Clair (Open Source)
Used by Quay.io registry. Scans images in registry.
Anchore
Policy-based scanner. Define policies about what’s allowed:
# policy.yaml
rules:
- gate: vulnerabilities
trigger: package
action: stop
params:
severity: high
LEVEL 4
Securing Your Images
1. Use trusted base images
✅ Official images:
FROM node:18-alpine
✅ Verified publishers:
FROM bitnami/nginx:latest
❌ Random user images:
FROM someuser/customnginx # Who is someuser?
2. Use minimal base images
Smaller image = smaller attack surface:
# Full Ubuntu: 77MB, hundreds of packages
FROM ubuntu:22.04
# Alpine: 5MB, minimal packages
FROM alpine:3.18
# Distroless: ~2MB, only runtime, no shell
FROM gcr.io/distroless/static-debian11
3. Keep images updated
# Regularly rebuild with latest base
docker build --no-cache -t myapp:latest .
4. Scan during build
Add scanning to your CI/CD:
# .github/workflows/build.yml
- name: Build image
run: docker build -t myapp:latest .
- name: Scan for vulnerabilities
run: |
trivy image --severity HIGH,CRITICAL --exit-code 1 myapp:latest
5. Sign images
Docker Content Trust (DCT) ensures images haven’t been tampered with:
export DOCKER_CONTENT_TRUST=1
docker push myregistry.com/myapp:latest
# Requires signing key
6. Use multi-stage builds
Remove build tools from final image:
# Build stage
FROM node:18 AS builder
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build
# Production stage
FROM node:18-alpine
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
CMD ["node", "dist/server.js"]
Build tools (compilers, dev dependencies) stay in builder stage, not in final image.
LEVEL 5
Reading Scan Results
Example Trivy output:
nginx:latest (debian 11.6)
Total: 45 (HIGH: 3, CRITICAL: 1)
┌──────────────┬────────────────┬──────────┬────────┬─────────────────────┐
│ Library │ Vulnerability │ Severity │ Status │ Installed Ver │
├──────────────┼────────────────┼──────────┼────────┼─────────────────────┤
│ openssl │ CVE-2023-12345 │ CRITICAL │ fixed │ 1.1.1n-0+deb11u3 │
│ curl │ CVE-2023-54321 │ HIGH │ fixed │ 7.74.0-1.3+deb11u1 │
└──────────────┴────────────────┴──────────┴────────┴─────────────────────┘
What to do:
- CRITICAL vulnerabilities: Fix immediately. Update base image or rebuild.
- HIGH vulnerabilities: Fix soon. Schedule update.
- MEDIUM/LOW: Monitor. Fix in regular maintenance.
Fixing vulnerabilities:
# Before
FROM nginx:1.20 # Has CVEs
# After
FROM nginx:1.24 # Latest, patched
Or update specific packages:
FROM ubuntu:22.04
RUN apt-get update && \
apt-get install -y --no-install-recommends \
openssl=1.1.1n-0+deb11u4 && \
apt-get clean && \
rm -rf /var/lib/apt/lists/*