Excessive Data Exposure in Feathersjs with Dynamodb

Excessive Data Exposure in Feathersjs with Dynamodb

Excessive Data Exposure occurs when an API returns more data than the client is authorized or intended to see. In a Feathersjs service backed by DynamoDB, this commonly arises because the service layer does not enforce field-level or row-level filtering before returning results from DynamoDB queries. Feathersjs by design can expose entire database records, and when those records contain sensitive fields such as internal identifiers, administrative flags, or PII, the full payload may be sent to the client without restriction.

The combination of Feathersjs and DynamoDB amplifies this risk when developers rely on high-level ORM-like abstractions and omit explicit projection or filtering. For example, a find call that does not specify which attributes to return may cause DynamoDB to return all item attributes, and Feathersjs may then pass that data directly to the client. This can reveal sensitive fields such as password_hash, role, internal_notes, or other columns that should remain server-side. Additionally, if the service does not validate or scope the query based on the authenticated user’s permissions, an attacker may enumerate or infer relationships across records, leading to privilege escalation or sensitive data aggregation.

Consider a typical Feathersjs service definition that queries a DynamoDB table without projection:

const feathers = require('@feathersjs/feathers');
const app = feathers();
const {DynamoDBDocumentClient, ScanCommand} = require('@aws-sdk/lib-dynamodb');
const ddbClient = DynamoDBDocumentClient.from(new AWS.DynamoDB());

app.use('/records', {
  async find(params) {
    const command = new ScanCommand({
      TableName: 'AppRecords'
    });
    const { Items } = await ddbClient.send(command);
    // Risk: Items may contain sensitive fields; no filtering applied
    return Items;
  }
});

In this example, the scan returns every attribute from every item in the table. If any item contains sensitive data, it is exposed to the caller. Attackers can exploit this by making repeated calls or leveraging other endpoints to harvest information. Sensitive exposure is further compounded when the client-side consumer assumes the shape of the data and does not validate server responses.

Another common pattern involves using service hooks or middleware that inadvertently forward full query results. For instance, hooks that modify params but do not restrict the data returned can lead to unintentional disclosure:

app.use('/users', {
  async before(hook) {
    // Risk: hook.params.query may lack $select or filter logic
    const command = new ScanCommand({ TableName: 'UserProfiles' });
    const { Items } = await ddbClient.send(command);
    hook.result = Items;
    return hook;
  }
});

To mitigate Excessive Data Exposure in this context, you must ensure that every DynamoDB query and scan applies explicit attribute selection and respects user context. You should avoid broad scans where possible and instead use queries with key conditions, applying FilterExpression only when necessary for business logic, not for security. Projections must be enforced at the service layer before returning data to the client.

Dynamodb-Specific Remediation in Feathersjs

Remediation centers on enforcing strict attribute selection and row-level scoping in every DynamoDB interaction. Use ProjectionExpression to return only the attributes required by the client or by the caller’s authorization context. Combine this with FilterExpression for post-query filtering, but remember that FilterExpression does not reduce read capacity usage; ProjectionExpression is the correct mechanism to limit returned fields.

For authenticated scenarios, incorporate the user identity from the Feathersjs params to scope queries and limit data exposure. Avoid scanning the entire table; prefer Query with a partition key and sort key condition to minimize both cost and exposure surface.

Below are concrete, secure examples that demonstrate how to apply ProjectionExpression and context-aware scoping in Feathersjs services using the AWS SDK for DynamoDB:

const feathers = require('@feathersjs/feathers');
const app = feathers();
const {DynamoDBDocumentClient, QueryCommand} = require('@aws-sdk/lib-dynamodb');
const ddbClient = DynamoDBDocumentClient.from(new AWS.DynamoDB());

app.use('/records', {
  async find(params) {
    const { user } = params.auth;
    if (!user) {
      throw new Error('Unauthorized');
    }
    const command = new QueryCommand({
      TableName: 'UserRecords',
      KeyConditionExpression: 'user_id = :uid',
      ExpressionAttributeValues: {
        ':uid': user.sub
      },
      // Return only required attributes; sensitive fields are omitted
      ProjectionExpression: 'record_id, title, created_at, metadata'
    });
    const { Items } = await ddbClient.send(command);
    return Items || [];
  }
});
app.use('/admin/records', {
  async find(params) {
    const { user } = params.auth;
    // Ensure admin role is verified before allowing broader access
    if (!user || !user.role || user.role !== 'admin') {
      throw new Error('Forbidden');
    }
    const command = new ScanCommand({
      TableName: 'AppRecords',
      // Limit returned fields to reduce exposure
      ProjectionExpression: 'record_id, owner_id, status, updated_at'
    });
    const { Items } = await ddbClient.send(command);
    return Items || [];
  }
});

In addition to projection, validate and sanitize any incoming query parameters to prevent injection or malformed expressions. Enforce row-level checks by ensuring that non-admin users cannot query arbitrary partitions or perform scans. Use service hooks to inject user context into params and centralize security logic, ensuring every DynamoDB operation respects the principle of least privilege and minimizes data exposure.

Control Purpose Example in Feathersjs + DynamoDB
ProjectionExpression Return only necessary attributes ProjectionExpression: 'id, name, created_at'
KeyConditionExpression Limit query to specific partitions KeyConditionExpression: 'user_id = :uid'
ExpressionAttributeValues Prevent injection via placeholder binding ExpressionAttributeValues: { ':uid': user.sub }
Row-level scoping Ensure users only access their own data Use authenticated sub to scope queries
Role-based access Restrict sensitive endpoints to privileged roles Check user.role === 'admin' before broader scans

FAQ

  • How can I detect Excessive Data Exposure in my Feathersjs + DynamoDB API?
    Use middleBrick to scan your endpoint; it checks whether responses include sensitive fields beyond what the client requires and validates that projections and row-level scoping are consistently applied.
  • Does enabling field filtering in the client eliminate the risk?
    No. Client-side filtering does not prevent the server from returning sensitive data; controls must be enforced server-side in the service and DynamoDB queries.

Related CWEs: propertyAuthorization

CWE IDNameSeverity
CWE-915Mass Assignment HIGH

Frequently Asked Questions

How can I detect Excessive Data Exposure in my Feathersjs + DynamoDB API?
Use middleBrick to scan your endpoint; it checks whether responses include sensitive fields beyond what the client requires and validates that projections and row-level scoping are consistently applied.
Does enabling field filtering in the client eliminate the risk?
No. Client-side filtering does not prevent the server from returning sensitive data; controls must be enforced server-side in the service and DynamoDB queries.