Xpath Injection in Feathersjs
How Xpath Injection Manifests in Feathersjs
Xpath injection in Feathersjs applications typically occurs when user input is incorporated into XPath queries without proper sanitization. Feathersjs, being a flexible Node.js framework, allows developers to integrate with various data sources including databases that support XPath queries, such as XML-based storage systems or when using XPath within NoSQL queries.
The vulnerability manifests when Feathersjs services accept user input and directly inject it into XPath expressions. For example, consider a Feathersjs service that queries an XML database for user information:
const { authenticate } = require('@feathersjs/authentication');
const { BadRequest } = require('@feathersjs/errors');
class XmlService {
async find(params) {
const { username, password } = params.query;
// Vulnerable XPath query construction
const xpathQuery = `/users/user[username='${username}' and password='${password}']`;
// Execute XPath query against XML data
const result = await this.executeQuery(xpathQuery);
return result;
}
}In this Feathersjs service, an attacker could submit a username like admin' or '1'='1 which would transform the XPath query into:
/users/user[username='admin' or '1'='1' and password='anything']This bypasses authentication by always evaluating to true. Another common pattern in Feathersjs applications is when using XML-based configuration files or when integrating with SOAP services that use XPath internally:
const xpath = require('xpath');
const dom = require('xmldom').DOMParser;
class ConfigService {
async getConfig(params) {
const { configId } = params.query;
// Vulnerable: direct interpolation of user input
const xpathQuery = `/configurations/config[id='${configId}']`;
const document = new dom().parseFromString(xmlData);
const result = xpath.select(xpathQuery, document);
return result;
}
}Feathersjs developers often fall into this trap when building custom services that need to query XML data or when working with XML-based APIs. The framework's flexibility means there's no built-in protection against XPath injection, making it the developer's responsibility to sanitize inputs.
Feathersjs-Specific Detection
Detecting XPath injection vulnerabilities in Feathersjs applications requires examining how user input flows through your services and gets incorporated into queries. middleBrick's black-box scanning approach is particularly effective for this, as it tests the actual runtime behavior of your Feathersjs endpoints without requiring source code access.
When middleBrick scans a Feathersjs API, it specifically looks for endpoints that might be vulnerable to XPath injection by:
- Testing common XPath injection patterns like single quotes, double quotes, and boolean operators
- Analyzing parameter names that suggest XML/XPath usage (username, id, query, path, etc.)
- Checking for XML-based content types in responses
- Examining error messages that might reveal XPath syntax
For developers who want to proactively scan their Feathersjs applications, middleBrick can be integrated directly into your development workflow:
# Scan your Feathersjs API from the CLI
middlebrick scan https://api.yourapp.com/users
# Integrate into your CI/CD pipeline
middlebrick scan --fail-below B --output json https://staging.yourapp.com
# Use the GitHub Action in your Feathersjs project
- name: Scan API Security
uses: middleBrick/middleBrick-action@v1
with:
url: https://api.yourapp.com
fail-on-grade-below: B
Within your Feathersjs codebase, you can identify potential XPath injection points by searching for patterns like:
// Search for these patterns in your codebase:
const xpath = require('xpath');
const xmldom = require('xmldom');
// String concatenation with user input
const query = `/path/to/element[name='${userInput}']`;
// Template literals with user input
const query = `/path/to/element[name="${userInput}"]`;
middleBrick's scoring system specifically flags these issues with severity levels based on the potential impact. A Feathersjs service that allows authentication bypass through XPath injection would receive a critical severity rating, while data exposure through XPath queries might be rated high severity.
Feathersjs-Specific Remediation
Remediating XPath injection in Feathersjs applications requires a combination of input validation, parameterized queries, and proper escaping. Here are Feathersjs-specific approaches to secure your code:
1. Use Parameterized XPath Queries
Instead of string concatenation, use XPath's parameter substitution features:
const { BadRequest } = require('@feathersjs/errors');
const xpath = require('xpath');
const dom = require('xmldom').DOMParser;
class SecureXmlService {
async find(params) {
const { username, password } = params.query;
// Validate and sanitize inputs
if (!this.isValidInput(username) || !this.isValidInput(password)) {
throw new BadRequest('Invalid input detected');
}
const document = new dom().parseFromString(xmlData);
// Use parameterized XPath with proper escaping
const xpathQuery = `//user[username=$username and password=$password]`;
const result = xpath.select(xpathQuery, document, {
username: username,
password: password
});
return result;
}
isValidInput(input) {
// Allow only alphanumeric and basic punctuation
return /^[a-zA-Z0-9_.-]+$/.test(input);
}
}2. Input Validation and Whitelisting
Implement strict input validation in your Feathersjs services:
const { HookContext } = require('@feathersjs/feathers');
// Create a validation hook for XPath injection prevention
const validateXPathInput = () => {
return async (context: HookContext) => {
const { params } = context;
// Define allowed characters
const allowedPattern = /^[[email protected]]+$/;
// Check all query parameters
Object.values(params.query).forEach(value => {
if (typeof value === 'string' && !allowedPattern.test(value)) {
throw new Error('Input contains invalid characters');
}
});
return context;
};
};
// Apply to your Feathersjs service
class XmlService {
setup(app) {
this.before = {
find: [validateXPathInput()]
};
}
}3. Use Feathersjs Hooks for Centralized Security
Implement security checks as reusable hooks:
const { hooks } = require('@feathersjs/feathers');
// XPath injection prevention hook
const xpathSecurityHook = () => {
return async (context) => {
const { method, type, params } = context;
// Only apply to external queries
if (type === 'before' && method === 'find') {
const query = params.query || {};
// Check for suspicious patterns
const suspiciousPatterns = [
/'.*or.*=/i,
/'.*and.*=/i,
/'.*union.*'/i,
/'.*select.*'/i
];
for (const [key, value] of Object.entries(query)) {
if (typeof value === 'string' &&
suspiciousPatterns.some(pattern => pattern.test(value))) {
throw new Error('Potential XPath injection detected');
}
}
}
return context;
};
};
// Apply to all services
app.hooks({
before: {
all: [xpathSecurityHook()]
}
});
4. Alternative: Use Safe XML Libraries
Consider using libraries that provide safer XML querying:
const fastXmlParser = require('fast-xml-parser');
class SafeXmlService {
async find(params) {
const { userId } = params.query;
// Parse XML and query in memory
const parsedData = fastXmlParser.parse(xmlData);
// Use JavaScript filtering instead of XPath
const result = parsedData.users.user.find(u => u.id === userId);
return result;
}
}