Excessive Data Exposure on Docker
How Excessive Data Exposure Manifests in Docker
Excessive Data Exposure in Docker environments occurs when container images, Docker Compose configurations, or API endpoints running in containers inadvertently expose sensitive information. This manifests through several Docker-specific attack patterns that differ from traditional web application vulnerabilities.
One common manifestation is environment variable leakage. Docker containers often receive sensitive configuration data through environment variables, which may be exposed through /proc/self/environ or accidentally logged in container stdout. For example, a Node.js API running in Docker might expose database credentials if environment variables are improperly handled:
// Vulnerable: Exposes all environment variables in API response
app.get('/api/config', (req, res) => {
res.json(process.env); // <-- Security risk: full env dump
});
// Secure: Only expose non-sensitive config
app.get('/api/config', (req, res) => {
res.json({
apiVersion: process.env.API_VERSION,
featureFlags: process.env.FEATURE_FLAGS
});
});Another Docker-specific pattern involves Dockerfile secrets management. Developers sometimes commit secrets to Dockerfiles or use multi-stage builds that inadvertently expose credentials in final images. Consider this vulnerable pattern:
# Vulnerable: Secrets in final image
FROM node:18
ARG DB_PASSWORD
ENV DB_PASSWORD=$DB_PASSWORD
COPY . .
RUN npm install
# Secret remains in image history
EXPOSE 3000
CMD ["node", "server.js"]
# Secure: Use multi-stage build with secret cleanup
FROM node:18 AS builder
ARG DB_PASSWORD
RUN echo "Database password: $DB_PASSWORD" > secret.txt
FROM node:18
COPY --from=builder /app/dist ./dist
# Secret.txt never makes it to final image
EXPOSE 3000
CMD ["node", "server.js"]Docker Compose volume mounts also create exposure risks. Developers sometimes mount entire host directories into containers, exposing sensitive files. A typical vulnerable compose file:
# Vulnerable: Exposes host .env files
version: '3.8'
services:
api:
build: .
volumes:
- ./:/app # <-- Exposes all host files
environment:
- DB_PASSWORD=${DB_PASSWORD}
# Secure: Explicit volume mounts with read-only access
version: '3.8'
services:
api:
build: .
volumes:
- ./src:/app/src:ro # Read-only source code
- ./config:/app/config:ro # Specific config files
environment:
- DB_PASSWORD=${DB_PASSWORD}Health check endpoints in Docker containers often reveal excessive information. Many containers expose /health or /status endpoints that return detailed system information:
# Vulnerable: Detailed health endpoint
@app.route('/health')
def health():
return jsonify({
'status': 'healthy',
'uptime': time.time() - start_time,
'memory_usage': process.memory_info().rss,
'db_connection': db.connected,
'env': dict(os.environ) # <-- Massive data exposure
})
# Secure: Minimal health response
@app.route('/health')
def health():
return jsonify({
'status': 'healthy' if db.connected else 'degraded'
})Docker-Specific Detection
Detecting Excessive Data Exposure in Docker environments requires both static analysis of container configurations and dynamic scanning of running containers. middleBrick's Docker-specific detection capabilities focus on the unique attack surface that containers present.
Static Dockerfile analysis examines multi-stage builds, ARG/ENV usage, and COPY instructions to identify potential secret exposure paths. The scanner analyzes Docker image layers to detect:
- Secrets embedded in image history through build arguments
- Sensitive files copied into final images
- Exposed environment variables in health endpoints
- Insecure volume mount configurations
Dynamic container scanning tests running containers for exposed endpoints and data leakage. middleBrick's black-box approach tests unauthenticated endpoints without requiring credentials:
# Scan a Docker-hosted API endpoint
middlebrick scan https://api.example.com/v1 --output json
# Scan multiple endpoints from a Docker network
middlebrick scan https://api.example.com/v1 --scan-endpoints /v1/users,/v1/orders --output htmlThe scanner specifically looks for Docker-related exposure patterns:
| Detection Pattern | What It Finds | Risk Level |
|---|---|---|
| /proc/self/environ access | Environment variable dumping | High |
| Health endpoint analysis | Excessive system information | Medium |
| Volume mount inspection | Unrestricted file access | High |
| Docker API exposure | Unauthenticated Docker socket access | Critical |
| Container metadata leakage | Runtime environment details | Medium |
Docker Compose security analysis examines service configurations for exposure risks:
# middleBrick detects these issues in compose files:
version: '3.8'
services:
api:
build: .
ports:
- "3000:3000" # <-- Public exposure without auth
environment:
- DB_PASSWORD=${DB_PASSWORD}
# middleBrick flags: no secrets management, no network isolationThe tool also analyzes container network configurations to identify services exposed to public networks without proper authentication controls. This includes checking for default Docker bridge network exposure and insecure port mappings.
Docker-Specific Remediation
Remediating Excessive Data Exposure in Docker environments requires a combination of secure development practices, Docker-native features, and runtime configurations. The following Docker-specific solutions address the most common exposure patterns.
Secrets management with Docker Secrets eliminates environment variable exposure for multi-container applications:
# Use Docker Secrets for sensitive data
FROM node:18
# Create a non-root user for security
RUN addgroup --system appgroup && adduser --system appuser
USER appuser
# Use multi-stage build to minimize final image size
FROM node:18 AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
FROM node:18
ARG NODE_ENV=production
WORKDIR /app
# Copy only necessary files from builder stage
COPY --from=builder /app/node_modules ./node_modules
COPY . .
# Use Docker Secrets mount
RUN mkdir -p /run/secrets
VOLUME /run/secrets
EXPOSE 3000
CMD ["node", "server.js"]Runtime configuration with Docker Compose secrets:
version: '3.8'
services:
api:
build: .
secrets:
- db_password
- jwt_secret
environment:
- NODE_ENV=production
networks:
- app-network
deploy:
labels:
- "traefik.enable=false" # Disable public exposure
secrets:
db_password:
external: true
jwt_secret:
external: true
networks:
app-network:
driver: overlay
attachable: falseHealth endpoint hardening ensures minimal information disclosure:
// Secure health check with minimal exposure
func healthHandler(w http.ResponseWriter, r *http.Request) {
// Only return status code, no sensitive data
w.WriteHeader(http.StatusOK)
w.Write([]byte("healthy"))
}
// Register with Docker health check
func main() {
http.HandleFunc("/health", healthHandler)
// Docker health check interval: 30s, timeout: 10s, retries: 3
go func() {
time.Sleep(30 * time.Second)
http.Get("http://localhost:8080/health")
}()
log.Fatal(http.ListenAndServe(":8080", nil))
}Volume mount security prevents unauthorized file access:
# Secure volume mounts with read-only access
version: '3.8'
services:
api:
build: .
volumes:
- ./src:/app/src:ro # Read-only source code
- ./config:/app/config:ro # Specific config files
- /tmp/logs:/app/logs:rw # Write access only to logs
user: "1000:1000" # Non-root user
security_opt:
- no-new-privileges:true
cap_drop:
- ALL
cap_add:
- CHOWNNetwork isolation prevents unauthorized container access:
# Isolated networks with firewall rules
version: '3.8'
services:
api:
build: .
networks:
- internal
- public
deploy:
endpoint_mode: dnsrr
ports:
- "3000:3000" # Only accessible through load balancer
database:
image: postgres:15
networks:
- internal
environment:
- POSTGRES_PASSWORD_FILE=/run/secrets/db_password
secrets:
- db_password
deploy:
labels:
- "traefik.enable=false" # No public exposure
networks:
internal:
internal: true
public:
driver: overlayRelated CWEs: propertyAuthorization
| CWE ID | Name | Severity |
|---|---|---|
| CWE-915 | Mass Assignment | HIGH |