HIGH injection flawsfeathersjsdynamodb

Injection Flaws in Feathersjs with Dynamodb

Injection Flaws in Feathersjs with Dynamodb — how this specific combination creates or exposes the vulnerability

Injection flaws in a FeathersJS service backed by DynamoDB typically arise when input from the request is concatenated into DynamoDB expression attribute values or condition expressions without proper validation or parameterization. Unlike SQL, DynamoDB does not support parameterized queries in the same way, but the AWS SDK encourages passing expression attribute values separately from the expression string. If a FeathersJS hook or service method interpolates user-controlled data directly into these expressions, an attacker can manipulate attribute names, condition logic, or function arguments to change the intended data access.

FeathersJS abstracts service logic with hooks that often build query parameters from params.query or payload fields. When these values are forwarded into DynamoDB operations such as scan or query, dynamic construction of filter expressions can lead to unintended data exposure or privilege bypass. For example, a client-supplied field like userId might be embedded into a KeyConditionExpression string instead of being passed as an expression attribute value, enabling an attacker to enumerate or modify other users’ records.

Another common pattern involves using update or put with dynamic attribute paths derived from request input. If an attacker controls a key used in ExpressionAttributeNames or provides values that affect the update expression, they may modify system attributes, escalate permissions, or inject conditional logic that bypasses intended authorization checks.

Real-world attack patterns mirror issues cataloged in the OWASP API Top 10, particularly API1:2023 Broken Object Level Authorization and API5:2023 Injection. A vulnerable FeathersJS service might expose a DynamoDB table scan where the partition key is derived from an unvalidated query parameter. By altering the parameter, an attacker can perform a low-and-slow enumeration or extract sensitive records belonging to other users.

Consider a DynamoDB scan that builds a FilterExpression from user input:

const params = {
  TableName: 'users',
  FilterExpression: '#status = :status',
  ExpressionAttributeNames: {
    '#status': 'status'
  },
  ExpressionAttributeValues: {
    ':status': req.query.status
  }
};
const data = await docClient.scan(params).promise();

If req.query.status is used directly in the expression values map, it is treated as a literal value and not a code injection vector in this snippet. However, if the application were to construct the expression string itself, such as:

const filter = `#status = ${req.query.status}`;

then injection becomes possible. More critically, if the table name or index name is derived from user input, an attacker might target a different table or secondary index, leading to unauthorized data access.

LLM/AI Security considerations also apply: if an endpoint echoes or logs query parameters in model prompts or outputs, injection payloads might be captured in model context, leading to prompt injection or data exfiltration through the LLM channel. middleBrick’s LLM security checks can detect such exposure patterns during scans.

Dynamodb-Specific Remediation in Feathersjs — concrete code fixes

Remediation centers on strict input validation, using DynamoDB expression attributes correctly, and avoiding string interpolation for expression components. Always treat user input as values, never as expression syntax. Use placeholders in the expression string and bind values through ExpressionAttributeValues. Validate and whitelist field names before using them in ExpressionAttributeNames.

For query or scan operations, ensure the partition key is supplied explicitly from authenticated context rather than from client-controlled query parameters. For dynamic filtering, use a strict allowlist for attribute names and map them to safe placeholder names.

Example of a safe FeathersJS service hook that builds DynamoDB scan parameters:

const allowedFilters = new Set(['createdAt', 'status', 'email']);

app.service('users').hooks({
  before: {
    async find(context) {
      const query = context.params.query || {};
      const filterExpressionParts = [];
      const expressionAttributeNames = {};
      const expressionAttributeValues = {};

      if (query.status) {
        const placeholder = '#s';
        filterExpressionParts.push(`${placeholder} = :status`);
        expressionAttributeNames[placeholder] = 'status';
        expressionAttributeValues[':status'] = query.status;
      }

      if (query.createdAt) {
        const placeholder = '#c';
        filterExpressionParts.push(`${placeholder} >= :createdAt`);
        expressionAttributeNames[placeholder] = 'createdAt';
        expressionAttributeValues[':createdAt'] = query.createdAt;
      }

      const filterExpression = filterExpressionParts.length > 0
        ? filterExpressionParts.join(' AND ')
        : undefined;

      context.params.dynamodb = {
        ExpressionAttributeNames: expressionAttributeNames,
        ExpressionAttributeValues: expressionAttributeValues,
        FilterExpression: filterExpression
      };
      // Remove client-supplied filter keys to avoid misuse by downstream handlers
      delete context.params.query.status;
      delete context.params.query.createdAt;
      return context;
    }
  }
});

In this pattern, the service only allows known-safe attributes to be used in filter expressions. The actual DynamoDB operation in the FeathersJS adapter passes these prebuilt parameters, ensuring user input never directly shapes the expression string.

For update operations, validate attribute paths rigorously. Do not allow client input to define which fields to update. Instead, map sanitized keys to predefined update expressions:

const updateMappings = {
  status: 'SET #s = :val',
  email: 'SET #e = :val'
};

app.service('users').hooks({
  before: {
    async update(id, data, context) {
      const updates = [];
      const expressionAttributeNames = {};
      const expressionAttributeValues = {};

      if (data.status !== undefined) {
        updates.push(updateMappings.status);
        expressionAttributeNames['#s'] = 'status';
        expressionAttributeValues[':val'] = data.status;
      }
      if (data.email !== undefined) {
        updates.push(updateMappings.email);
        expressionAttributeNames['#e'] = 'email';
        expressionAttributeValues[':val'] = data.email;
      }

      if (updates.length === 0) {
        throw new Error('No valid fields to update');
      }

      const updateExpression = updates.join(', ');
      const params = {
        TableName: 'users',
        Key: { id: { S: id } },
        UpdateExpression: updateExpression,
        ExpressionAttributeNames: expressionAttributeNames,
        ExpressionAttributeValues: expressionAttributeValues,
        ReturnValues: 'UPDATED_NEW'
      };

      await context.params.dynamodb.update(params).promise();
      // Continue with remaining service logic
      return context;
    }
  }
});

These examples illustrate how to keep expression construction deterministic and safe. By using middleBrick’s dashboard and CLI to scan these services regularly, you can verify that no injection-prone patterns remain. The Pro plan’s continuous monitoring can alert you if new risky patterns are introduced in future changes, and the GitHub Action can enforce a minimum score threshold before merging.

Finally, always apply principle of least privilege to the IAM role associated with the Lambda or service executing DynamoDB calls. Even with hardened expressions, limiting write access to specific attributes and read access to necessary indexes reduces the impact of any remaining vulnerability.

Frequently Asked Questions

Can injection flaws in FeathersJS with DynamoDB lead to authentication bypass?
Yes. If an attacker can manipulate key condition expressions or filter logic, they may bypass ownership checks and access or modify other users’ records, effectively bypassing authentication controls enforced at the application layer.
Does using DynamoDB expressions fully prevent injection risks in FeathersJS?
Using expression attribute values and strict attribute name whitelisting significantly reduces risk, but injection flaws can still arise if expression strings are constructed dynamically with untrusted input. Always validate and map inputs rather than interpolating them into expression text.