Insecure Direct Object Reference on Docker
How Insecure Direct Object Reference Manifests in Docker
Insecure Direct Object Reference (IDOR) in Docker environments typically occurs when applications fail to properly validate user permissions when accessing Docker resources. This manifests in several Docker-specific attack patterns.
# Vulnerable: Direct container ID exposure
@app.route('/containers//logs')
def get_container_logs(container_id):
client = docker.from_env()
container = client.containers.get(container_id) # No permission check!
return container.logs()
This endpoint allows any authenticated user to access logs from any container by simply changing the container_id parameter. An attacker could enumerate container IDs and extract sensitive information from containers they shouldn't have access to.
# Docker API endpoint enumeration
curl -s http://docker-host/containers/json | jq -r '.[].Id' | head -5
# Returns: 12345678... 87654321...
Another common pattern involves Docker volume mounts:
# Vulnerable: Path traversal via volume mounts
@app.route('/volumes//read')
def read_volume_file(volume_path):
with open(f'/mnt/volumes/{volume_path}', 'r') as f:
return f.read()
An attacker could traverse directories using ../ sequences to access files outside the intended volume mount point, potentially exposing host system files or other containers' data.
Docker Compose files often contain IDOR vulnerabilities when services reference shared resources without proper isolation:
# Vulnerable: Shared database access without tenant isolation
version: '3.8'
services:
web_app:
build: .
environment:
- DB_HOST=database
- DB_NAME=shared_db
- DB_USER=shared_user
- DB_PASS=shared_password
When multiple tenants share the same database without tenant-specific filtering, one tenant can access another's data by simply knowing or guessing the primary key values.
Docker-Specific Detection
Detecting IDOR in Docker environments requires examining both the Docker API surface and application code that interfaces with Docker resources. Here's how to identify these vulnerabilities:
# Scan Docker daemon API for exposed endpoints
curl -s http://localhost:2375/containers/json | jq '.[] | {Id: .Id, Names: .Names}'
Check if your Docker daemon is listening on a TCP socket without authentication. The default Unix socket is secure, but a TCP socket on port 2375 (unencrypted) or 2376 (TLS) may expose your Docker API to network attacks.
For application-level detection, examine your code for these patterns:
# Check for direct resource access without permission validation
import ast
def find_idor_vulnerabilities(filepath):
with open(filepath) as f:
tree = ast.parse(f.read())
issues = []
for node in ast.walk(tree):
if isinstance(node, ast.Call):
# Look for patterns like client.containers.get() without permission checks
if (isinstance(node.func, ast.Attribute) and
node.func.attr in ['get', 'list'] and
'containers' in node.func.value.id):
issues.append({
'line': node.lineno,
'vulnerable_function': node.func.attr,
'issue': 'Potential IDOR - no permission validation'
})
return issues
middleBrick's Docker-specific scanning examines these patterns automatically:
# Scan a Dockerized API endpoint
middlebrick scan https://api.example.com --docker
The scanner tests for container enumeration, volume traversal, and network service exposure by attempting authenticated and unauthenticated access to Docker resources. It specifically checks for:
| Test Category | Description | Docker-Specific Check |
|---|---|---|
| Container Access | Can unauthorized users access container data? | Attempts to access containers by ID enumeration |
| Volume Traversal | Can users escape volume mount boundaries? | Tests path traversal in volume paths |
| Network Exposure | Are Docker network services exposed? | Scans for exposed Docker daemon ports |
| Resource Enumeration | Can users list all resources? | Tests for unrestricted container/volume listing |
Network-level detection is also critical:
# Check for exposed Docker daemon
nmap -p 2375,2376 docker-host
# Check for exposed container ports
docker ps --format 'table {{.Names}}\t{{.Ports}}'
Docker-Specific Remediation
Remediating IDOR vulnerabilities in Docker environments requires both Docker configuration changes and application-level security controls. Here are specific fixes using Docker's native features:
# Secure Dockerfile with proper user permissions
FROM node:18-alpine
# Create non-root user
RUN addgroup -g 1001 -S nodejs
RUN adduser -S nodejs -u 1001
# Set proper permissions on application directory
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
RUN chown -R nodejs:nodejs /app
# Switch to non-root user
USER nodejs
# Bind to non-privileged port
EXPOSE 3000
CMD ["node", "server.js"]
This Dockerfile prevents container breakout by running as a non-root user, limiting the impact if an IDOR vulnerability is exploited.
For application-level fixes, implement proper permission validation:
# Secure container access with permission validation
from functools import wraps
def require_container_access(container_id):
def decorator(f):
@wraps(f)
def wrapper(*args, **kwargs):
# Validate user has access to this container
user = get_current_user()
if not user.can_access_container(container_id):
return {'error': 'Access denied'}, 403
return f(*args, **kwargs)
return wrapper
return decorator
@app.route('/containers//logs')
@require_container_access
def get_container_logs(container_id):
client = docker.from_env()
container = client.containers.get(container_id)
return container.logs()
Implement tenant isolation in Docker Compose:
# Multi-tenant database isolation
version: '3.8'
services:
web_app:
build: .
environment:
- DB_HOST=database
- DB_NAME=${TENANT_ID}_db
- DB_USER=${TENANT_ID}_user
- DB_PASS=${TENANT_ID}_password
secrets:
- source: db_password_${TENANT_ID}
- source: db_user_${TENANT_ID}
secrets:
db_password_tenant1:
file: ./secrets/tenant1_password.txt
db_user_tenant1:
file: ./secrets/tenant1_user.txt
Use Docker's built-in network segmentation:
# Create isolated networks for different user groups
docker network create --driver bridge --internal restricted_network
# Only allow specific containers to connect to sensitive networks
docker run -d --name sensitive_app \
--network restricted_network \
--network-alias sensitive_service \
your_app_image
Implement API rate limiting at the Docker daemon level:
# Use nginx as a reverse proxy with rate limiting
upstream docker_api {
server docker-host:2375;
}
server {
listen 80;
server_name docker-proxy;
# Rate limit to prevent enumeration attacks
limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s;
location /containers/ {
limit_req zone=api burst=20 nodelay;
proxy_pass http://docker_api;
}
}
Related CWEs: bolaAuthorization
| CWE ID | Name | Severity |
|---|---|---|
| CWE-250 | Execution with Unnecessary Privileges | HIGH |
| CWE-639 | Insecure Direct Object Reference | CRITICAL |
| CWE-732 | Incorrect Permission Assignment | HIGH |