Xml External Entities in Hapi with Dynamodb
Xml External Entities in Hapi with Dynamodb — how this specific combination creates or exposes the vulnerability
XML External Entity (XXE) injection occurs when an XML parser processes external entity references embedded in XML data provided by an attacker. In a Hapi application that parses XML input and interacts with Amazon DynamoDB, this becomes a high-impact chain: untrusted XML reaches a parser, the parser resolves external entities, and the server-side request can expose metadata, trigger SSRF against internal services, or leak data before it ever reaches DynamoDB.
Hapi applications often accept structured payloads; if an endpoint accepts XML (for example, consuming an uploaded file or a SOAP-style body), a vulnerable XML parser can be tricked into reading local files via file:// URLs, requesting internal metadata from the EC2 instance metadata service, or causing excessive network calls that lead to denial of service. Because DynamoDB is typically accessed using the AWS SDK for JavaScript, the exploit path is indirect: the attacker does not directly manipulate DynamoDB queries, but the side effects of XXE (such as SSRF to internal endpoints, credential exposure, or file reads) can facilitate further compromise of the environment where DynamoDB credentials are available.
For example, an attacker could provide an XML body with a DOCTYPE defining an external entity that points to http://169.254.169.254/latest/meta-data/iam/security-credentials/. If the Hapi server follows the external reference, it may inadvertently leak IAM instance profile credentials. Those credentials, if accidentally logged or exposed, could allow an attacker to make unauthorized calls to DynamoDB using the SDK. Another variant uses entity expansion (Billion Laughs) to consume server memory and CPU during XML parsing, indirectly affecting the availability of DynamoDB operations handled by the same service.
Because middleBrick scans the unauthenticated attack surface and tests input validation and SSRF in parallel, it can surface such risks when an API endpoint processes XML without disabling external entity resolution. The scan does not fix the parsing behavior, but it highlights the need to treat XML input as untrusted and to enforce strict parser settings before any DynamoDB interaction occurs.
Dynamodb-Specific Remediation in Hapi — concrete code fixes
Remediation focuses on preventing the XML parser from loading external entities and ensuring that any data sent to DynamoDB is validated and sanitized. Below are concrete Hapi handler examples using the official AWS SDK for JavaScript v3, with parser hardening and safe usage patterns.
1. Disable external entities in the XML parser
If you must accept XML, use a parser that does not resolve external entities. With xmldom, avoid enabling external references:
const { DOMParser } = require('@xmldom/xmldom');
function parseXmlSafely(xmlString) {
const parser = new DOMParser({
// Ensure no external subset or entity resolution
externalReferences: { allowExternalResources: false, allowDTD: false }
});
return parser.parseFromString(xmlString, 'text/xml');
}
2. Validate and sanitize before DynamoDB operations
Use Joi or another schema validator for incoming data, and only then construct DynamoDB commands. This ensures unexpected XML-derived values do not affect request parameters.
const Joi = require('joi');
const { DynamoDBClient, PutItemCommand } = require('@aws-sdk/client-dynamodb');
const itemSchema = Joi.object({
pk: Joi.string().pattern(/^user#[0-9]+$/).required(),
sk: Joi.string().pattern(/^profile$/).required(),
email: Joi.string().email().required()
});
const client = new DynamoDBClient({ region: 'us-east-1' });
async function createItem(validated) {
const params = {
TableName: process.env.DYNAMODB_TABLE,
Item: {
pk: { S: validated.pk },
sk: { S: validated.sk },
email: { S: validated.email }
}
};
await client.send(new PutItemCommand(params));
return { status: 'ok' };
}
// In a Hapi route
server.route({
method: 'POST',
path: '/items',
handler: async (request, h) => {
const xml = request.payload;
const doc = parseXmlSafely(xml);
// Extract and normalize fields safely
const extracted = {
pk: doc.getElementsByTagName('pk')[0]?.textContent || '',
sk: doc.getElementsByTagName('sk')[0]?.textContent || '',
email: doc.getElementsByTagName('email')[0]?.textContent || ''
};
const { error, value } = itemSchema.validate(extracted);
if (error) {
return h.response({ error: error.details.map(d => d.message) }).code(400);
}
await createItem(value);
return h.response({ success: true }).code(201);
}
});
3. Apply AWS SDK best practices
Use environment variables for credentials and avoid assuming IAM roles via instance metadata when possible. If you must rely on instance metadata, ensure the metadata service is not reachable via SSRF by restricting outbound paths at the network level and validating any URLs your code constructs.
// Ensure the SDK uses safe credential sources
const client = new DynamoDBClient({
// region via env, credentials via standard AWS mechanisms
// Do not pass credentials directly from parsed XML
});
4. Reject XML when not required
If your API does not need XML, do not add XML parsers. Prefer JSON and enforce strict content-type checks to reduce the attack surface related to XXE and parser misuse.