Xml External Entities in Hapi with Firestore
Xml External Entities in Hapi with Firestore — how this specific combination creates or exposes the vulnerability
An XML External Entity (XXE) attack occurs when an XML processor discloses internal files or triggers network calls via malicious external entity definitions. In a Hapi server, if user-controlled data is parsed as XML and the parser is configured to resolve external entities, an attacker can coerce the server to read local files, reach internal endpoints, or cause denial of service. When Firestore is used as the backend, the risk is not that Firestore itself parses XML, but that a Hapi service deserializes XML input (for example, from an HTTP payload or an uploaded file) and then uses Firestore operations based on that data. If the XML contains external entity references, the server may disclose configuration or source files that include Firestore project settings, service account key paths, or environment variables that affect Firestore access. An attacker can also leverage XXE to perform SSRF against internal Firestore metadata endpoints or service metadata, potentially inferring project structure or service permissions. Because Firestore rules and IAM policies are often defined with project-specific context, leaking server-side paths or service account details can make it easier to craft authenticated attacks against the database. In a typical API flow, a Hapi route might accept an XML upload, parse it with an unsafe XML parser, and then write values into Firestore. If the parser resolves DOCTYPE declarations and external entities, the server can be tricked into reading files such as /etc/passwd or local instance metadata that may contain sensitive configuration related to Firestore authentication. This combination therefore exposes the vulnerability through insecure XML handling in Hapi rather than through Firestore itself, but the impact is amplified when Firestore holds sensitive project data.
Firestore-Specific Remediation in Hapi — concrete code fixes
To prevent XXE in Hapi when working with Firestore, ensure XML parsing is either disabled or configured to disallow external entities. Use a well-maintained XML parser with secure defaults and avoid legacy or permissive options that resolve DOCTYPEs. The following examples show safe approaches for Hapi routes that may receive XML input.
Example 1: Reject or sanitize XML input
If your API does not need XML, reject XML content types early:
const Hapi = require('@hapi/hapi');
const init = async () => {
const server = Hapi.server({ port: 4000, host: 'localhost' });
server.route({
method: 'POST',
path: '/submit',
options: {
parse: {
payload: false // Disable parsing to avoid processing XML
},
handler: (request, h) => {
// Expect JSON only
const { data } = request.payload;
// Validate and use Firestore SDK with trusted data
return { status: 'ok' };
}
}
});
await server.start();
console.log('Server running on %s', server.info.uri);
};
init();
Example 2: Secure XML parsing with an XML parser that disables external entities
If you must process XML, use a parser that disables external entities. For example, with libxmljs (Node.js), configure the parser securely:
const Hapi = require('@hapi/hapi');
const libxmljs = require('libxmljs');
const init = async () => {
const server = Hapi.server({ port: 4000, host: 'localhost' });
server.route({
method: 'POST',
path: '/parse-xml',
options: {
parse: {
payload: true
},
handler: (request, h) => {
const xmlPayload = request.payload.toString();
try {
// Parse XML without loading external entities
const doc = libxmljs.parseXml(xmlPayload, {
noblanks: true,
noent: false,
nocdata: false,
forbidExternalEntities: true // Disable external entities
});
// Extract only needed data and validate before Firestore operations
const title = doc.get('//title/text()') ? doc.get('//title/text()').value() : null;
if (!title) {
throw new Error('Missing required field');
}
// Safe Firestore usage with trusted, validated data
// const docRef = firestore.collection('items').doc();
// await docRef.set({ title });
return { message: 'XML processed safely', title };
} catch (err) {
throw Boom.badRequest('Invalid or unsafe XML: ' + err.message);
}
}
}
});
await server.start();
console.log('Server running on %s', server.info.uri);
};
init();
Example 3: Firestore write with validated, non-XML data
After validating input, interact with Firestore using the official SDK. Keep sensitive configuration server-side and avoid echoing raw user input into Firestore paths or document fields:
const Hapi = require('@hapi/hapi');
const admin = require('firebase-admin');
// Initialize Firestore securely using environment-bound credentials
admin.initializeApp({
credential: admin.credential.applicationDefault()
});
const firestore = admin.firestore();
const init = async () => {
const server = Hapi.server({ port: 4000, host: 'localhost' });
server.route({
method: 'POST',
path: '/items',
options: {
parse: {
payload: true
},
handler: async (request, h) => {
const { name, value } = request.payload;
// Validate and sanitize inputs
if (typeof name !== 'string' || name.trim().length === 0) {
throw Boom.badRequest('Invalid name');
}
if (typeof value !== 'number') {
throw Boom.badRequest('Value must be a number');
}
// Write validated data to Firestore
const docRef = firestore.collection('items').doc();
await docRef.set({ name: name.trim(), value, createdAt: admin.firestore.FieldValue.serverTimestamp() });
return { id: docRef.id, status: 'created' };
}
}
});
await server.start();
console.log('Server running on %s', server.info.uri);
};
init();
Additional hardening tips
- Do not log raw XML payloads or Firestore credentials in production.
- Apply strict content-type validation and size limits on incoming payloads.
- Use Firestore IAM and rules to enforce least privilege; do not rely on parsing logic alone for security decisions.
- Keep server-side libraries and the Firestore SDK up to date to avoid known parser vulnerabilities.