HIGH integrity failuresexpressdynamodb

Integrity Failures in Express with Dynamodb

Integrity Failures in Express with Dynamodb — how this specific combination creates or exposes the vulnerability

Integrity failures occur when an API does not enforce proper checks on data ownership and authorization, allowing one user to read, modify, or delete another user’s data. In Express applications that use DynamoDB as a persistence layer, these failures often arise from incomplete authorization logic combined with the way DynamoDB modeling encourages broad-scope queries and key-based access patterns.

DynamoDB’s primary key structure (partition key and optional sort key) influences how data is retrieved. When access controls are applied only at the service or table level, and not enforced per request based on the authenticated user, endpoints can inadvertently expose other users’ items. For example, an endpoint like GET /users/:userId/profile might construct a DynamoDB key using userId from the URL without verifying that the userId matches the authenticated principal. Because DynamoDB returns the item if the key exists, the API returns data that should be restricted, resulting in a Broken Level of Authorization (BOLA) / Insecure Direct Object Reference (IDOR).

Additionally, DynamoDB’s flexible query patterns can exacerbate integrity issues. A query that uses a Global Secondary Index (GSI) on an attribute like ownerId without also filtering by the authenticated user’s ID can return multiple records belonging to other users. If the application layer does not cross-check the requestor’s identity against each returned item, data exposure occurs. This is especially risky when combined with features like DynamoDB Streams or automated backups, where stale or replicated data may be processed without re-evaluating authorization.

The combination of Express routing that trusts client-supplied identifiers and DynamoDB’s key-based retrieval creates a common attack surface. Attackers can manipulate URL parameters, tamper with JSON Web Token claims, or exploit weak session management to change the userId in requests. Because DynamoDB does not perform application-level authorization, it will faithfully return data for valid keys even when the request lacks proper context. Without explicit checks in Express middleware, integrity violations become likely and may be missed during development.

These issues align with OWASP API Top 10 categories, particularly BOLA and data exposure. They may also affect compliance mappings for PCI-DSS and SOC2 when sensitive records are accessible across users. middleBrick scans detect these patterns by correlating OpenAPI path parameters and DynamoDB key structures with authentication and authorization checks, highlighting where identity verification is missing or inconsistent.

Dynamodb-Specific Remediation in Express — concrete code fixes

To prevent integrity failures, Express APIs using DynamoDB must enforce ownership checks on every request that references a specific item. This means validating that the authenticated user’s identity matches the item’s owner before performing any read, update, or delete operation. The following patterns demonstrate how to implement this in code.

1. Enforce user ownership on item retrieval

Always include the authenticated user’s ID when querying DynamoDB, rather than relying solely on a route parameter. This ensures that even if an attacker changes the userId in the URL, the query will not return another user’s data.

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

app.get('/users/:userId/profile', async (req, res) => {
  const authenticatedUserId = req.user.sub; // from JWT or session
  const requestedUserId = req.params.userId;

  if (authenticatedUserId !== requestedUserId) {
    return res.status(403).json({ error: 'Forbidden: cannot access other user data' });
  }

  const params = {
    TableName: 'Users',
    Key: {
      userId: authenticatedUserId
    }
  };

  try {
    const data = await dynamo.get(params).promise();
    res.json(data.Item);
  } catch (err) {
    res.status(500).json({ error: 'Failed to fetch profile' });
  }
});

2. Use ownerId in queries with GSIs

When using Global Secondary Indexes, include the authenticated user’s ID in both the index key design and the query filter. This prevents cross-user data retrieval even when querying by secondary attributes.

const params = {
  TableName: 'Messages',
  IndexName: 'ownerMessageIndex',
  KeyConditionExpression: 'ownerId = :uid AND timestamp BETWEEN :start AND :end',
  ExpressionAttributeValues: {
    ':uid': authenticatedUserId,
    ':start': '2024-01-01T00:00:00Z',
    ':end': '2024-12-31T23:59:59Z'
  }
};

const data = await dynamo.query(params).promise();
res.json(data.Items);

3. Validate ownership before updates and deletes

Before performing update or delete operations, re-check ownership by reading the existing item or including the owner identifier in the key condition. Avoid using only client-supplied identifiers for destructive actions.

const params = {
  TableName: 'Tasks',
  Key: {
    taskId: req.params.taskId
  }
};

const task = await dynamo.get(params).promise();
if (!task.Item || task.Item.ownerId !== authenticatedUserId) {
  return res.status(404).json({ error: 'Not found or forbidden' });
}

const updateParams = {
  TableName: 'Tasks',
  Key: { taskId: req.params.taskId },
  UpdateExpression: 'set status = :s',
  ExpressionAttributeValues: { ':s': 'completed' },
  ReturnValues: 'UPDATED_NEW'
};

await dynamo.update(updateParams).promise();
res.json({ status: 'updated' });

4. Apply middleware for centralized checks

Use Express middleware to verify ownership for a group of routes, reducing duplication and ensuring consistent enforcement across the API.

function ensureOwnership(modelKey) {
  return (req, res, next) => {
    const authenticatedUserId = req.user.sub;
    const resourceUserId = req.params[modelKey] || req.body[modelKey];
    if (authenticatedUserId !== resourceUserId) {
      return res.status(403).json({ error: 'Forbidden' });
    }
    next();
  };
}

app.patch('/users/:userId/settings', ensureOwnership('userId'), (req, res) => {
  // safe to proceed
});

5. Design DynamoDB tables to support ownership queries

Model your DynamoDB schema so that queries by ownerId are efficient and naturally scoped. Favor composite keys such as PK = USER#userId and SK = METADATA#... , or use a GSI with ownerId as the partition key to enable authorized scans without scanning the entire table.

Frequently Asked Questions

Why does using route parameters like :userId in Express with DynamoDB create integrity risks?
Because DynamoDB will return the item if the key exists, regardless of whether the authenticated user is allowed to access it. Trusting client-supplied identifiers without verifying ownership leads to BOLA/IDOR issues. Always cross-check the authenticated user’s identity against the item’s owner attribute before performing any operation.
How can middleBrick help detect integrity failures in an Express + DynamoDB API?
middleBrick scans the OpenAPI specification and runtime behavior to identify mismatches between authentication, path parameters, and DynamoDB key usage. It flags endpoints where ownership checks are missing or inconsistent, providing prioritized findings and remediation guidance aligned with frameworks like OWASP API Top 10.