Xpath Injection on Docker
How Xpath Injection Manifests in Docker
Xpath injection in Docker environments typically occurs when container applications process XML data without proper input validation, allowing attackers to manipulate Xpath queries to access unauthorized data or execute arbitrary code. Docker's containerization model can actually amplify certain Xpath injection risks by exposing XML processing endpoints to network services that wouldn't normally be exposed in traditional deployments.
In Dockerized applications, Xpath injection often manifests through REST APIs that accept XML payloads and use Xpath expressions to query internal data stores. Consider a Node.js application running in a container that processes XML-based configuration files:
const express = require('express');
const { DOMParser } = require('xmldom');
const xpath = require('xpath');
const app = express();
app.use(express.text({ type: 'application/xml' }));
app.post('/api/config', (req, res) => {
const xml = req.body;
const doc = new DOMParser().parseFromString(xml);
// Vulnerable Xpath query with user-controlled input
const query = req.query.path || '//config/setting';
const nodes = xpath.select(query, doc);
res.json({ settings: nodes.map(n => n.textContent) });
});
This pattern is particularly dangerous in Docker because containers often expose such endpoints on all network interfaces by default. An attacker could send:
POST /api/config?path=//config/setting[1]/text()|//admin/password/text() HTTP/1.1
Content-Type: application/xml
<config>
<setting>value</setting>
</config>Another Docker-specific manifestation occurs when containers mount XML configuration files from the host system and process them with user-controlled Xpath expressions. This creates a path traversal risk:
const fs = require('fs');
const { DOMParser } = require('xmldom');
app.post('/api/mount', (req, res) => {
const mountPath = req.body.mountPath || '/app/config.xml';
const xml = fs.readFileSync(mountPath, 'utf8');
const doc = new DOMParser().parseFromString(xml);
// User controls the Xpath query
const query = req.body.xpathQuery || '//config/setting';
const nodes = xpath.select(query, doc);
res.json({ results: nodes.map(n => n.textContent) });
});
In Docker Compose environments, Xpath injection can also occur through service-to-service communication where one container processes XML data received from another container. The isolation between containers doesn't prevent Xpath injection if the application logic is vulnerable.
Docker-Specific Detection
Detecting Xpath injection in Docker environments requires both runtime monitoring and static analysis of container images. For runtime detection, middleBrick's Docker-specific scanning identifies Xpath injection vulnerabilities by testing XML endpoints with malicious payloads:
middlebrick scan https://api.example.com/config --format=json
{
"score": 72,
"category_breakdown": {
"input_validation": {
"score": 45,
"severity": "high",
"vulnerabilities": [
{
"type": "XPATH_INJECTION",
"description": "User-controlled Xpath query detected",
"remediation": "Validate and sanitize Xpath queries using a whitelist approach",
"severity": "high",
"docker_specific": true
}
]
}
}
}Static analysis in Docker environments should examine package.json files for vulnerable Xpath libraries and scan mounted volumes for XML processing code. Use Docker's security scanning tools to identify known vulnerable Xpath libraries:
docker run --rm -v $(pwd):/app node:18 npm audit
# Check for vulnerable Xpath packages
npm list | grep xpath
For containerized applications, implement request validation middleware that inspects XML payloads before processing:
const express = require('express');
const { DOMParser } = require('xmldom');
const xpath = require('xpath');
function validateXmlPayload(req, res, next) {
if (req.is('application/xml')) {
try {
const doc = new DOMParser().parseFromString(req.body);
// Basic validation - reject if contains script tags or dangerous elements
const scripts = xpath.select('//script', doc);
if (scripts.length > 0) {
return res.status(400).json({ error: 'Invalid XML content' });
}
} catch (err) {
return res.status(400).json({ error: 'Malformed XML' });
}
}
next();
}
app.use(validateXmlPayload);
Docker Compose files should be audited for volume mounts that expose XML configuration files to potentially untrusted containers. Look for patterns like:
services:
api:
volumes:
- ./config:/app/config:ro # Read-only is safer but still requires validation
environment:
- CONFIG_PATH=/app/config/settings.xmlDocker-Specific Remediation
Remediating Xpath injection in Docker environments requires a defense-in-depth approach that combines input validation, secure coding practices, and container security configurations. The most effective remediation is to eliminate user-controlled Xpath queries entirely by using parameterized queries or predefined query templates.
Here's a Docker-specific remediation pattern using a whitelist of allowed Xpath queries:
const express = require('express');
const { DOMParser } = require('xmldom');
const xpath = require('xpath');
// Whitelist of allowed Xpath queries
const allowedQueries = {
'settings': '//config/setting',
'users': '//users/user',
'config': '//config/*'
};
app.post('/api/config', (req, res) => {
const xml = req.body;
const doc = new DOMParser().parseFromString(xml);
const queryKey = req.query.query || 'settings';
const query = allowedQueries[queryKey];
if (!query) {
return res.status(400).json({ error: 'Invalid query type' });
}
const nodes = xpath.select(query, doc);
res.json({ results: nodes.map(n => n.textContent) });
});
For Docker Compose deployments, implement network segmentation to isolate XML processing services from untrusted networks. Use Docker's built-in security features:
services:
xml-processor:
image: node:18-alpine
security_opt:
- no-new-privileges:true
read_only: true
tmpfs:
- /tmp:rw,noexec,nosuid,size=100m
environment:
- NODE_ENV=production
networks:
- internal
api-gateway:
image: nginx:alpine
ports:
- "443:443"
networks:
- external
depends_on:
- xml-processor
networks:
internal:
internal: true
external:
driver: bridgeImplement XML schema validation before Xpath processing to ensure only expected XML structures are processed:
const { DOMParser, XMLSerializer } = require('xmldom');
const xpath = require('xpath');
const xml2js = require('xml2js');
function validateXmlSchema(xml, schema) {
try {
const parser = new xml2js.Parser({
validator: (err) => { throw err; }
});
parser.parseString(xml);
return true;
} catch (err) {
return false;
}
}
app.post('/api/secure-config', (req, res) => {
const xml = req.body;
// Validate against expected schema
if (!validateXmlSchema(xml, configSchema)) {
return res.status(400).json({ error: 'Invalid XML structure' });
}
const doc = new DOMParser().parseFromString(xml);
const nodes = xpath.select('//config/setting', doc);
res.json({ settings: nodes.map(n => n.textContent) });
});
For multi-container applications, use Docker secrets to store sensitive Xpath configurations and API keys rather than hardcoding them in container images:
services:
secure-api:
image: node:18-alpine
secrets:
- xpath-config
environment:
- XPATH_CONFIG_FILE=/run/secrets/xpath-config
secrets:
xpath-config:
file: ./secrets/xpath-config.json