Xpath Injection in Feathersjs with Dynamodb
Xpath Injection in Feathersjs with Dynamodb — how this specific combination creates or exposes the vulnerability
XPath Injection is a classic injection class that typically applies to XML processing, but similar injection logic can manifest when user-controlled input is used to construct query expressions for NoSQL databases that allow dynamic filtering. FeathersJS is a popular real-time framework that abstracts services via a common interface, and it can be configured with a Dynamodb adapter to interact with AWS DynamoDB. The risk arises when a FeathersJS service uses raw user input to build expression attribute values or condition expressions passed to the DynamoDB client, especially if the service relies on dynamic lookup or filtering without strict validation.
Consider a FeathersJS service that retrieves user records by a custom identifier such as a display name. If the implementation builds a DynamoDB Scan or Query condition by concatenating user input into an expression, an attacker can inject additional logical segments. For example, a developer might write a find method that constructs an expression like user_name = ':username' and binds the value directly. If the binding is implemented via string interpolation rather than parameterized attribute values, an attacker can terminate the intended expression and append new conditions, such as ' OR begins_with(user_name, 'a') AND 'x'='x. In a FeathersJS hook or service method, this could be triggered via crafted query parameters that are forwarded to the DynamoDB adapter.
Because DynamoDB does not use XPath, the injection is not XPath in the traditional XML sense; instead, it is an expression injection into the parameters passed to the DynamoDB client. The FeathersJS layer can inadvertently expose this if it passes untrusted data into condition expressions, projection expressions, or even attribute path segments. Additionally, if the service uses dynamic key names derived from user input, an attacker may attempt to manipulate the structure of the request to bypass intended access controls. The combination of FeathersJS’s flexible service hooks and the DynamoDB low-level client means that developers must explicitly validate and sanitize all inputs before constructing request parameters, otherwise the unauthenticated attack surface tested by middleBrick may surface injection findings under categories such as Input Validation and Property Authorization.
Using middleBrick’s scan on a FeathersJS endpoint backed by DynamoDB can reveal these risks by probing filter parameters and inspecting whether injected expressions alter the logical outcome of queries. The scanner does not fix the implementation, but it provides findings with severity and remediation guidance to help developers refactor dynamic expression construction to use safe patterns.
Dynamodb-Specific Remediation in Feathersjs — concrete code fixes
To prevent expression injection when using DynamoDB in FeathersJS, always use parameterized expressions rather than string concatenation. DynamoDB’s SDK supports expression attribute names and expression attribute values, which ensure that user input is never interpreted as part of the expression syntax. Below are concrete, working examples that demonstrate secure patterns for query and scan operations within a FeathersJS service.
Secure Query with Expression Parameters
Assume a users service where you want to retrieve items by a partition key user_id and optionally filter by a sort key prefix. The secure approach uses the ExpressionAttributeNames and ExpressionAttributeValues parameters:
const feathers = require('@feathersjs/feathers');
const app = feathers();
const {DynamoDBDocumentClient, ScanCommand, QueryCommand} = require('@aws-sdk/lib-dynamodb');
const {dynamodbClient} = require('./dynamodb-client'); // configured client
app.use('/users', {
async find(params) {
const userId = params.query.userId;
const filterPrefix = params.query.prefix || '';
const command = new QueryCommand({
TableName: process.env.USERS_TABLE,
KeyConditionExpression: 'user_id = :uid AND begins_with(sort_key, :prefix)',
ExpressionAttributeNames: {
'#uid': 'user_id',
'#sk': 'sort_key'
},
ExpressionAttributeValues: {
':uid': { S: userId },
':prefix': { S: filterPrefix }
},
Limit: 10
});
const client = DynamoDBDocumentClient.from(dynamodbClient);
const response = await client.send(command);
return response.Items;
}
});
This pattern ensures that userId and filterPrefix are passed as values, not as part of the expression string. Even if an attacker provides a value like abc' OR begins_with(sort_key, 'x') AND ''x'='x, it will be treated as a plain string value for :prefix, not as executable expression syntax.
Secure Scan with Filter Expression
If you must scan (which is less efficient), use a filter expression with attribute values and avoid dynamic attribute names derived from user input:
app.use('/reports', {
async find(params) {
const status = params.query.status;
const command = new ScanCommand({
TableName: process.env.REPORTS_TABLE,
FilterExpression: '#status = :statusVal',
ExpressionAttributeNames: {
'#status': 'status'
},
ExpressionAttributeValues: {
':statusVal': { S: status }
}
});
const client = DynamoDBDocumentClient.from(dynamodbClient);
const response = await client.send(command);
return response.Items;
}
});
For advanced use cases involving dynamic sort keys or partial updates, validate and normalize attribute paths on the server side. Do not allow raw user input to dictate expression attribute names without a strict allowlist. middleBrick’s checks for Property Authorization and Input Validation can highlight endpoints where such unsafe patterns remain in place.