HIGH broken access controlfeathersjsdynamodb

Broken Access Control in Feathersjs with Dynamodb

Broken Access Control in Feathersjs with Dynamodb — how this specific combination creates or exposes the vulnerability

Broken Access Control occurs when an API fails to enforce proper authorization checks, allowing one user to access or modify another user's resources. The combination of Feathersjs and Amazon DynamoDB can unintentionally expose this vulnerability when authorization logic is omitted or misapplied at the service or hook layer. Feathersjs is a framework that encourages a service-based architecture where each service exposes CRUD operations. If a service definition does not enforce record-level ownership checks, an attacker can modify or retrieve arbitrary records by guessing or enumerating identifiers, such as DynamoDB primary key values.

DynamoDB itself is a low-level NoSQL store that does not enforce application-level permissions. It simply returns items based on key queries. Therefore, if a Feathersjs service queries DynamoDB using a user-supplied ID without validating that the item belongs to the requesting user, the application exhibits a Broken Access Control flaw, commonly referenced as IDOR (Insecure Direct Object Reference) or BOLA (Broken Object Level Authorization). For example, a GET request to /users/12345 might directly query a DynamoDB table with a key condition like user_id = :uid. If the :uid value is taken directly from the request without verifying that it matches the authenticated user's ID, an attacker can change the identifier to access another user's data.

Additionally, because Feathersjs can auto-generate service routes and map query parameters to DynamoDB expressions, misconfigured hooks or implicit parameter binding can lead to privilege escalation. A developer might use a generic find or get method without scoping results to the authenticated user's partition key. In DynamoDB, data is often partitioned by a user identifier (e.g., user_id) to isolate tenants. If the partition key is not enforced in every query, an attacker may perform a horizontal privilege escalation across users. Vertical privilege escalation can occur if an endpoint intended for standard users inadvertently allows administrative actions because role-based checks are missing in the hook or service logic.

OpenAPI specifications and runtime findings from middleBrick can highlight these issues by comparing declared parameters and required security schemes with actual runtime behavior. For instance, if an operation claims to require authentication but does not include a scope or role constraint in the parameters, the scan may flag missing authorization scoping. This is especially important for DynamoDB, where key schema design directly impacts access control feasibility. A table using a composite key with user_id as the partition key and record_id as the sort key must enforce partition key scoping in application logic, as DynamoDB does not do this automatically.

Dynamodb-Specific Remediation in Feathersjs — concrete code fixes

To remediate Broken Access Control in Feathersjs with DynamoDB, you must enforce ownership checks at the service hook level and ensure all queries are scoped to the authenticated user. Below are concrete code examples that demonstrate secure patterns.

First, ensure your Feathers service hooks extract the user identity from the authentication payload and use it to constrain DynamoDB queries. The following hook adds the authenticated user's ID to the query parameters, ensuring that every find and get operation is scoped to that user.

// src/hooks/user-scoped-queries.js
module.exports = function () {
  return async context => {
    const { user } = context.params;
    if (user && user.sub) {
      // Ensure all queries are scoped to the authenticated user
      context.params.dynamodb = {
        ExpressionAttributeNames: { '#uid': 'user_id' },
        ExpressionAttributeValues: { ':uid': user.sub },
        FilterExpression: '#uid = :uid'
      };
    }
    return context;
  };
};

Next, define your Feathers service with explicit parameters that prevent implicit mapping. Use a custom get method to verify ownership before retrieving a DynamoDB item. This example shows how to fetch an item only if its partition key matches the authenticated user.

// src/services/records/records.class.js
const { Service } = require('feathers-dynamodb');

class RecordsService extends Service {
  async get(id, params) {
    const { user } = params;
    if (!user || !user.sub) {
      throw new Error('Unauthorized');
    }
    const params = {
      Key: {
        user_id: user.sub,
        record_id: id
      },
      TableName: process.env.DYNAMODB_TABLE
    };
    const result = await this.app.get('dynamodb').get(params).promise();
    if (!result.Item || result.Item.user_id !== user.sub) {
      throw new Error('Not found');
    }
    return result.Item;
  }
}

module.exports = function (app) {
  const options = {
    name: 'records',
    Model: RecordsService,
    paginate: {
      default: 10,
      max: 50
    }
  };
  app.use('/records', new RecordsService(options));
  const service = app.service('records');
  service.hooks({
    before: {
      all: [require('./hooks/user-scoped-queries')()],
      find: [],
      get: [async context => {
        // Additional scoping for get requests
        context.params.dynamodb = context.params.dynamodb || {};
        context.params.dynamodb.KeyConditionExpression = 'user_id = :uid AND record_id = :id';
        context.params.dynamodb.ExpressionAttributeValues = {
          ...context.params.dynamodb.ExpressionAttributeValues,
          ':uid': context.params.user.sub,
          ':id': context.id
        };
        return context;
      }]
    },
    after: []
  });
};

For find operations that return multiple items, enforce partition key scoping in the query to prevent horizontal privilege escalation. The following hook modifies the DynamoDB query to include a key condition expression that binds the partition key to the authenticated user.

// src/hooks/dynamodb-key-scoping.js
module.exports = function () {
  return async context => {
    const { user } = context.params;
    if (user && user.sub) {
      const table = process.env.DYNAMODB_TABLE;
      context.params.dynamodb = {
        TableName: table,
        KeyConditionExpression: 'user_id = :uid',
        ExpressionAttributeValues: { ':uid': user.sub }
      };
    }
    return context;
  };
};

These hooks ensure that every interaction with DynamoDB is constrained by the authenticated user's identity. Combined with role-based checks in your application logic, this approach mitigates both IDOR and privilege escalation risks. middleBrick can validate these protections by scanning your endpoints and confirming that authorization scoping is consistently applied across all service methods.

Frequently Asked Questions

How can I verify that my Feathersjs service properly scopes queries to the authenticated user?
Use middleBrick to scan your API endpoints. It validates that query parameters and DynamoDB key expressions include user-scoped filters and that no endpoint relies solely on client-supplied identifiers without ownership checks.
What role does DynamoDB key design play in preventing Broken Access Control?
Design your table with user_id as the partition key to isolate data by tenant. Enforce this partition key in every query within Feathersjs hooks, because DynamoDB does not automatically restrict access to items with the same sort key across different partition keys.