HIGH identification failuresfeathersjsdynamodb

Identification Failures in Feathersjs with Dynamodb

Identification Failures in Feathersjs with Dynamodb — how this combination creates or exposes the vulnerability

Identification failures occur when an API fails to properly enforce identity and access management checks, allowing one user to act as another. The combination of Feathersjs and Amazon DynamoDB can unintentionally enable this class of vulnerability when dynamic data from the request is used to construct DynamoDB key expressions without strict ownership scoping. Feathersjs is a framework that favors flexibility, and if service hooks and parameters are not hardened, an attacker can manipulate identifiers such as id, userId, or custom path variables to reference records that belong to other users.

In a typical Feathersjs service configured with the Feathers DynamoDB adapter, a developer might map a route like /users/:id directly to a DynamoDB query using the parameter as the key. If the adapter uses the incoming params.query to build a KeyConditionExpression without verifying that the requested identifier belongs to the authenticated subject, the service may return another user’s item. This is a BOLA (Broken Object Level Authorization) pattern, which middleBrick categorizes under its BOLA/IDOR check. The scanner tests whether an unauthenticated or low-privilege context can retrieve or enumerate records by manipulating identifiers, and it reports findings with severity and remediation guidance.

DynamoDB’s schema-less design amplifies the risk. Because primary keys are defined by partition and sort key attributes, incorrect composition of key expressions can lead to scanning beyond the intended partition or returning more items than expected. For example, using a sort key prefix that is attacker-controlled can expose multiple records within a partition. The scanner’s checks include Property Authorization and Input Validation to detect whether queries are constrained by the authenticated user’s identity, and it cross-references these runtime findings against the OpenAPI spec to ensure declared parameters align with actual authorization logic.

Additional risk patterns emerge when services rely on client-supplied keys for delete or update operations without verifying ownership. A delete route that accepts an id from the URL and passes it directly to a DeleteCommand can be exploited if the caller can guess or iterate through identifiers. middleBrick’s BFLA/Privilege Escalation and Unsafe Consumption checks examine such flows, ensuring that authorization is re-evaluated at the point of mutation and that responses do not leak sensitive data through error messages or metadata.

The LLM/AI Security checks highlight a further vector: if endpoints that interact with DynamoDB are exposed as tools to AI agents without proper constraints, an attacker could prompt the system to enumerate or modify records through indirect calls. middleBrick tests for System Prompt Leakage and Active Prompt Injection to ensure that tool-use patterns do not inadvertently expose identification logic or allow unauthorized data exfiltration through LLM interfaces.

To illustrate a vulnerable pattern, consider a Feathers service that builds a DynamoDB query from route parameters without scoping to the authenticated user:

const { DynamoDBClient, GetItemCommand } = require('@aws-sdk/client-dynamodb');
const client = new DynamoDBClient({ region: 'us-east-1' });

app.service('records').find({
  params: {
    query: {
      id: params.id // directly using user-supplied ID
    }
  }
});

In this example, if params.id is not validated against the authenticated subject, the service may retrieve any record whose primary key matches the supplied value. A secure implementation would instead enforce ownership by combining the authenticated user’s ID with the requested identifier and using a conditional expression to ensure they match.

Dynamodb-Specific Remediation in Feathersjs — concrete code fixes

Remediation focuses on binding every DynamoDB operation to the authenticated subject and validating input against the known schema. In Feathersjs, this is achieved by augmenting params in a before hook to include the authenticated user’s identity and constructing key expressions that incorporate this trusted subject rather than relying on client-provided identifiers alone.

For read operations, use a KeyConditionExpression that includes both the partition key derived from the user context and the sort key supplied by the client. This ensures that even if a client manipulates the sort key, the query is confined to the authenticated user’s partition.

// Secure Feathers hook for DynamoDB get
const { DynamoDBClient, GetItemCommand } = require('@aws-sdk/client-dynamodb');
const { marshall } = require('@aws-sdk/util-dynamodb');

const client = new DynamoDBClient({ region: 'us-east-1' });

function secureGetHook(context) {
  const { params } = context;
  const { user } = params;
  const { id } = params.query;

  if (!user || !user.sub) {
    throw new Error('Unauthenticated');
  }

  const paramsCommand = {
    TableName: process.env.TABLE_NAME,
    Key: marshall({
      userId: user.sub,      // partition key derived from authenticated subject
      recordId: id           // sort key from client, but scoped within user partition
    })
  };

  return context;
}

// Usage in service
app.service('records').hooks({
  before: {
    get: [secureGetHook]
  }
});

For queries that involve searching within a user’s partition, explicitly set the partition key in the KeyConditionExpression and avoid allowing the client to dictate it. The following example demonstrates a find hook that scopes results to the authenticated user:

const { DynamoDBClient, QueryCommand } = require('@aws-sdk/client-dynamodb');
const { marshall, unmarshall } = require('@aws-sdk/util-dynamodb');

const client = new DynamoDBClient({ region: 'us-east-1' });

async function scopedQueryHook(context) {
  const { params } = context;
  const { user } = params;
  const { sortValue, limit } = params.query;

  if (!user || !user.sub) {
    throw new Error('Unauthenticated');
  }

  const command = new QueryCommand({
    TableName: process.env.TABLE_NAME,
    KeyConditionExpression: 'userId = :uid AND sortKey begins_with(:sort)',
    ExpressionAttributeValues: marshall({
      ':uid': user.sub,
      ':sort': sortValue || ''
    }),
    Limit: limit || 10
  });

  const { Items } = await client.send(command);
  context.result = { data: unmarshall(Items) };
  return context;
}

app.service('records').hooks({
  before: {
    find: [scopedQueryHook]
  }
});

For mutations such as update or delete, recompute identifiers on the server side and reject requests where the client-supplied identifier does not match the authenticated context. Enforce that any id in the request body or query aligns with the user’s records before constructing a ConditionExpression or proceeding with the command.

const { DynamoDBClient, UpdateItemCommand } = require('@aws-sdk/client-dynamodb');
const { marshall, unmarshall } = require('@aws-sdk/util-dynamodb');

const client = new DynamoDBClient({ region: 'us-east-1' });

async function secureUpdateHook(context) {
  const { params } = context;
  const { user } = params;
  const { id, status } = params.body;

  if (!user || !user.sub) {
    throw new Error('Unauthenticated');
  }

  // Ensure the record belongs to the user before updating
  const checkKey = marshall({
    userId: user.sub,
    recordId: id
  });

  const updateParams = {
    TableName: process.env.TABLE_NAME,
    Key: checkKey,
    UpdateExpression: 'set #s = :status',
    ExpressionAttributeNames: { '#s': 'status' },
    ExpressionAttributeValues: marshall({ ':status': status }),
    ReturnValues: 'UPDATED_NEW'
  };

  const command = new UpdateItemCommand(updateParams);
  const { Attributes } = await client.send(command);
  context.result = { data: unmarshall(Attributes) };
  return context;
}

app.service('records').hooks({
  before: {
    update: [secureUpdateHook]
  }
});

These examples illustrate how to bind DynamoDB operations to the authenticated subject, validate inputs, and avoid direct use of client-controlled keys as the sole identifier. By combining these patterns with middleBrick’s checks for Authentication, Property Authorization, and Input Validation, developers can reduce the likelihood of identification failures in production deployments.

CheckRelevance to Identification Failures
AuthenticationEnsures requests include valid credentials before data access
Property AuthorizationValidates that properties and keys are constrained by user context
Input ValidationConfirms that identifiers conform to expected formats and ownership

Frequently Asked Questions

Can middleBrick detect identification failures in API configurations that use DynamoDB?
Yes, middleBrick scans unauthenticated attack surfaces and includes checks such as BOLA/IDOR, Property Authorization, and Input Validation to identify identification failures when API endpoints interact with DynamoDB.
Does middleBrick provide guidance on how to bind DynamoDB queries to authenticated users in Feathersjs?
Yes, findings include prioritized remediation guidance with code examples that demonstrate how to scope DynamoDB operations to the authenticated subject in Feathersjs services.