Prototype Pollution on Docker
How Prototype Pollution Manifests in Docker
Prototype Pollution in Docker environments typically emerges through Node.js applications running inside containers, where malicious input can modify JavaScript object prototypes. This vulnerability allows attackers to inject arbitrary properties into objects, potentially leading to Remote Code Execution (RCE) or data manipulation.
Common Docker-specific attack vectors include:
- Malicious package dependencies in
package.jsonthat contain prototype pollution vulnerabilities - Untrusted user input processed by Express.js middleware in containerized applications
- Third-party npm modules with prototype pollution flaws, often introduced via
npm installin Docker build steps - Configuration files or environment variables that get merged into JavaScript objects without proper sanitization
Consider this vulnerable Docker setup:
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
EXPOSE 3000
CMD ["node", "server.js"]If server.js contains code like:
const express = require('express');
const app = express();
app.use(express.json());
app.post('/update', (req, res) => {
const config = { ...req.body }; // Vulnerable: no prototype check
res.json({ success: true });
});
app.listen(3000);An attacker can send:
POST /update HTTP/1.1
Host: localhost:3000
Content-Type: application/json
{
"__proto__": {
"isAdmin": true
}
}This modifies the prototype of all objects, potentially escalating privileges across the application. In Docker contexts, this becomes particularly dangerous because containers often run with elevated privileges or have access to sensitive host resources through mounted volumes.
Another Docker-specific scenario involves malicious Dockerfiles that use RUN npm install with compromised packages. An attacker could publish a package to npm that contains prototype pollution vulnerabilities, which then gets installed during the Docker build process:
RUN npm install vulnerable-package@^1.0.0Once built, the container runs with these vulnerabilities baked in, making them persistent across deployments.
Docker-Specific Detection
Detecting Prototype Pollution in Docker environments requires both static analysis of your container images and dynamic runtime scanning. Here are Docker-specific detection strategies:
Static Analysis with Snyk and npm audit
Integrate security scanning into your Dockerfile build process:
FROM node:18-alpine AS base
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
# Security scan stage
FROM base AS security-scan
RUN npm audit --audit-level=high
RUN npm install -g snyk
RUN snyk test --severity-threshold=highRuntime Scanning with middleBrick
middleBrick's black-box scanning approach is particularly effective for Dockerized APIs. Since it requires no credentials or configuration, you can scan your containerized API endpoints directly:
npx middlebrick scan https://your-docker-api:3000middleBrick tests for Prototype Pollution by attempting to modify object prototypes through various input vectors and checking if those modifications persist. The scanner's Property Authorization check specifically looks for improper handling of special properties like __proto__, constructor, and prototype.
Docker Layer Inspection
Examine your Docker image layers for suspicious npm packages:
docker history your-image:latest
docker run --rm -it your-image:latest npm list --depth=0Look for packages with low download counts, recent publish dates, or names that mimic popular packages (typosquatting).
Automated Scanning in CI/CD
Integrate middleBrick into your GitHub Actions workflow for Docker applications:
name: Security Scan
on: [push, pull_request]
jobs:
security:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Build Docker image
run: docker build -t your-app .
- name: Scan with middleBrick
run: |
docker run -d -p 3000:3000 your-app
npx middlebrick scan http://localhost:3000 --fail-below=BThis setup fails your build if the security score drops below a B grade, preventing vulnerable Docker images from being deployed.
Docker-Specific Remediation
Remediating Prototype Pollution in Docker environments requires a multi-layered approach that addresses both the application code and the container build process.
Code-Level Fixes
Implement prototype pollution protection in your Node.js applications:
// Safe object merge function
function safeMerge(target, source) {
const isProtoPolluted = Object.keys(source).some(key =>
key === '__proto__' || key === 'constructor' || key === 'prototype'
);
if (isProtoPolluted) {
throw new Error('Prototype pollution attempt detected');
}
return { ...target, ...source };
}
// Use in Express middleware
app.use(express.json({
verify: (req, res, buf) => {
const parsed = JSON.parse(buf.toString());
const dangerousKeys = ['__proto__', 'constructor', 'prototype'];
if (dangerousKeys.some(key => key in parsed)) {
throw new Error('Prototype pollution attempt');
}
}
}));Dockerfile Security Enhancements
Update your Dockerfile to include security scanning and safe practices:
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production --ignore-scripts
# Security scanning
RUN npm audit --audit-level=high
RUN npm install -g snyk && snyk test --severity-threshold=high
# Copy application code
COPY . .
# Production stage
FROM node:18-alpine AS production
WORKDIR /app
COPY --from=builder /app ./
# Drop to non-root user
RUN addgroup -g 1001 -S nodejs
RUN adduser -S nodejs -u 1001
USER nodejs
EXPOSE 3000
CMD ["node", "server.js"]Dependency Management
Implement strict dependency controls in your Docker builds:
# package.json with security constraints
{
"name": "your-app",
"dependencies": {
"express": "^4.18.0",
"helmet": "^7.1.0"
},
"overrides": {
"prototype-pollution-npm": ["1.0.0"]
},
"resolutions": {
"prototype-pollution-npm": "none"
}
}Runtime Protection
Add runtime protection against prototype pollution in your Docker container:
// Prototype pollution protection middleware
const prototypePollutionProtection = (req, res, next) => {
const dangerousKeys = ['__proto__', 'constructor', 'prototype'];
const checkObject = (obj) => {
for (const key in obj) {
if (dangerousKeys.includes(key)) {
console.warn('Prototype pollution attempt detected:', key);
return res.status(400).json({ error: 'Invalid input' });
}
if (typeof obj[key] === 'object' && obj[key] !== null) {
checkObject(obj[key]);
}
}
};
checkObject(req.body);
checkObject(req.query);
checkObject(req.params);
next();
};
app.use(prototypePollutionProtection);
app.use(express.json());Monitoring and Alerting
Integrate middleBrick's continuous monitoring for production Docker deployments:
docker run -d \
--name middlebrick-monitor \
-e MIDDLEBRICK_API_KEY=your-key \
-e TARGET_URL=http://your-api:3000 \
-e SCAN_INTERVAL=3600 \
-e ALERT_WEBHOOK=https://your-slack-webhook \
middlebrick/monitor:latestThis container continuously scans your API every hour and sends alerts if prototype pollution vulnerabilities are detected or if the security score drops.