HIGH dictionary attackfeathersjsdynamodb

Dictionary Attack in Feathersjs with Dynamodb

Dictionary Attack in Feathersjs with Dynamodb — how this specific combination creates or exposes the vulnerability

A dictionary attack against a Feathersjs service backed by DynamoDB leverages two factors: the framework’s default RESTful routes and DynamoDB’s behavior under unauthenticated or poorly constrained queries. Feathersjs exposes CRUD-like endpoints (e.g., /users) that map to service methods. If authentication or authorization is missing or misconfigured, an attacker can enumerate identifiers by observing timing differences or response content. DynamoDB does not leak whether an item exists in the same way a relational database might; however, conditional reads and query patterns can introduce observable timing variance or error responses that an attacker can interpret.

Consider a typical Feathersjs service definition without proper access control:

const { Service } = require('feathers-mongodb');
// Risky: no authentication hook, no custom find filter limiting scope
app.use('/accounts', Service({
  Model: context.app.get('sequelize'),
  paginate: { default: 10, max: 100 }
}));

When DynamoDB is used as the persistence layer (e.g., via a custom adapter or ORM), an attacker can send a large set of guessed identifiers (user IDs, email addresses, or API keys) via the find or get endpoints. Each request results in a DynamoDB GetItem or Query operation. Even if the service returns a generic error for missing items, subtle differences in response time or status codes (e.g., ConditionalCheckFailedException vs. successful Query) can reveal valid identifiers. This is especially relevant when the partition key design uses predictable values (e.g., USER#{email}) and the request is unauthenticated or uses a low-privilege IAM role.

Common misconfigurations that amplify risk include:

  • Missing authentication on the service hook, allowing unauthenticated enumeration.
  • Overly permissive IAM policies attached to the DynamoDB credentials used by the service, enabling broad read queries.
  • Predictable key schema that allows attackers to iterate through likely keys without triggering account lockouts.

For example, an endpoint like GET /users?email=attacker@example.com may perform a DynamoDB Query with a filter on the email attribute. If the service does not enforce ownership or scope restrictions, the attacker can iterate over likely emails and infer which ones exist based on response behavior, effectively conducting a dictionary attack.

Because middleBrick scans the unauthenticated attack surface, it can detect such endpoints and highlight findings related to Authentication, BOLA/IDOR, and Input Validation. These findings map to OWASP API Top 10 (e.g., Broken Object Level Authorization) and common misconfigurations in serverless and NoSQL architectures.

Dynamodb-Specific Remediation in Feathersjs — concrete code fixes

Remediation focuses on ensuring every request is authenticated and scoped, using DynamoDB’s conditional checks and query constraints to avoid information leakage. Below are concrete patterns you can apply in a Feathersjs service.

1. Enforce authentication and scope queries to the authenticated user

Use a before hook to inject the requester’s identity and restrict DynamoDB operations to their partition key. This prevents horizontal enumeration across users.

const { authenticate } = require('@feathersjs/authentication').hooks;

app.use('/accounts', new Service({
  Model: context.app.get('dynamodbModel'),
  paginate: { default: 10, max: 10 }
}));

app.service('accounts').hooks({
  before: {
    all: [authenticate('jwt')],
    find: [context => {
      // Scope query to the authenticated user’s partition key
      context.params.query.userId = context.params.user.sub; // from JWT
      // Ensure the client cannot override this filter
      delete context.params.query.$limit;
      delete context.params.query.$skip;
    }]
  }
});

2. Use DynamoDB ConditionExpression to prevent unauthorized updates

When updating or deleting an item, use a ConditionExpression that includes the partition key and a check on an ownership attribute. This ensures that even if an attacker guesses an ID, they cannot act on it without the correct attributes.

const AWS = require('aws-sdk');
const dynamo = new AWS.DynamoDB.DocumentClient();

async function safeUpdate(userId, accountId, updateData) {
  const params = {
    TableName: 'Accounts',
    Key: { id: accountId },
    UpdateExpression: 'set #nm = :nm, #st = :st',
    ConditionExpression: 'userId = :uid',
    ExpressionAttributeNames: { '#nm': 'name', '#st': 'status' },
    ExpressionAttributeValues: {
      ':nm': updateData.name,
      ':st': updateData.status,
      ':uid': userId
    }
  };
  try {
    await dynamo.update(params).promise();
  } catch (err) {
    if (err.code === 'ConditionalCheckFailedException') {
      throw new Error('Not authorized');
    }
    throw err;
  }
}

3. Avoid leaking existence via error handling and consistent responses

Design your service layer to return uniform responses for missing items and handle DynamoDB errors internally. Do not expose ConditionalCheckFailedException details to the client.

app.service('accounts').hooks({
  after: {
    all: [context => {
      // Normalize responses to avoid timing differences
      if (context.result && context.result.items) {
        // ensure consistent shape
      }
      return context;
    }],
    error: [context => {
      if (context.error && context.error.code === 'ConditionalCheckFailedException') {
        context.error = new Error('Forbidden');
        context.error.code = 403;
      }
      return Promise.resolve(context);
    }]
  }
});

4. Apply fine-grained IAM policies for the service role

Restrict the IAM role used by middleBrick or your runtime to least privilege: allow GetItem/Query only on the specific table with a condition on the partition key attribute. This reduces the impact of compromised credentials.

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "dynamodb:GetItem",
        "dynamodb:Query"
      ],
      "Resource": "arn:aws:dynamodb:region:account-id:table/Accounts",
      "Condition": {
        "ForAllValues:StringEquals": {
          "dynamodb:LeadingKeys": ["${aws:userId}"]
        }
      }
    }
  ]
}

By combining scoped queries, conditional writes, careful error handling, and least-privilege IAM, you mitigate dictionary attack vectors against Feathersjs services backed by DynamoDB. middleBrick can help identify endpoints where these controls are missing through its checks for Authentication, BOLA/IDOR, and Input Validation.

Frequently Asked Questions

Can a dictionary attack succeed even if responses are uniform?
Yes, if timing differences exist due to DynamoDB read capacity or network latency, attackers can infer validity. Always enforce authentication and scope queries to remove user-specific variability.
Does enabling DynamoDB encryption at rest stop dictionary attacks?
No. Encryption at rest protects data if storage is compromised; it does not prevent unauthorized query patterns or identity enumeration. Use authentication, scoped queries, and condition checks instead.