HIGH insecure direct object referenceexpressdynamodb

Insecure Direct Object Reference in Express with Dynamodb

Insecure Direct Object Reference in Express with Dynamodb — how this specific combination creates or exposes the vulnerability

Insecure Direct Object Reference (BOLA/IDOR) occurs when an API exposes internal object references such as database keys or IDs in requests and relies solely on client-supplied values to locate resources. In an Express service that uses DynamoDB as the persistence layer, this commonly maps to an endpoint like /users/:userId/profile where the route parameter userId is passed directly to a DynamoDB GetItem or Query without verifying that the requesting identity is authorized for that specific item.

Because DynamoDB does not enforce row-level ownership in the API layer, the onus is on the application to enforce authorization. If the Express route only checks that the userId exists in DynamoDB and returns the item, an attacker can iterate or manipulate identifiers (e.g., incrementing numeric IDs or guessing UUIDs) to access other users’ data. This becomes a BOLA/IDOR when authorization is missing or incomplete, even if the request is authenticated. MiddleBrick’s BOLA/IDOR checks flag this by correlating unauthenticated or low-privilege runtime calls with the OpenAPI definition to detect missing ownership checks on resource identifiers.

DynamoDB-specific exposures in Express often arise from these patterns:

  • Using a client-provided key (e.g., userId, postId) directly in params.Key without scoping to the requesting user or tenant.
  • Relying on client-supplied filter expressions or secondary indexes without validating that the requester is allowed to query that subset of data.
  • Exposing predictable keys via URLs or client-side code, which enables enumeration and chaining with other endpoints.

For example, an Express route that forwards req.params.postId into a DynamoDB GetItem without ensuring the post belongs to the requesting user creates a straightforward IDOR. MiddleBrick’s unauthenticated scan surface may detect such endpoints by testing known IDs when no proper authorization context is enforced, highlighting the risk in the BOLA/IDOR category alongside remediation guidance mapped to frameworks such as OWASP API Top 10 and SOC2 controls.

Dynamodb-Specific Remediation in Express — concrete code fixes

Remediation centers on enforcing ownership or tenant boundaries on every DynamoDB access and avoiding direct exposure of internal keys in URLs. Below are concrete, executable patterns for Express routes using the AWS SDK for JavaScript v3.

1. Always scope queries to the authenticated subject. If your user identity is available on req.user (e.g., from a session or JWT), use it as a partition key or filter condition rather than trusting the client-supplied ID alone.

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

app.get("/users/:userId/profile", async (req, res) => {
  // Enforce that the requesting user can only access their own profile
  const requestingUserId = req.user.sub; // from auth context
  const targetUserId = req.params.userId;

  if (requestingUserId !== targetUserId) {
    return res.status(403).json({ error: "Forbidden: cannot access other user’s profile" });
  }

  const command = new GetItemCommand({
    TableName: process.env.USERS_TABLE,
    Key: {
      userId: { S: targetUserId }
    }
  });

  try {
    const { Item } = await client.send(command);
    if (!Item) return res.status(404).json({ error: "Not found" });
    res.json({ userId: Item.userId.S, email: Item.email.S });
  } catch (err) {
    res.status(500).json({ error: "Server error" });
  }
});

2. Use a composite key design that includes the user identifier as part of the DynamoDB key to prevent cross-user access even if the ID is manipulated. For example, store userId#postId as the partition-sort key or enforce a filter expression that includes the user ID.

app.get("/users/:userId/posts/:postId", async (req, res) => {
  const userId = req.user.sub;
  const postId = req.params.postId;

  const command = new GetItemCommand({
    TableName: process.env.POSTS_TABLE,
    Key: {
      pk: { S: `USER#${userId}` },
      sk: { S: `POST#${postId}` }
    }
  });

  const { Item } = await client.send(command);
  if (!Item) return res.status(404).json({ error: "Not found" });
  res.json({ postId: Item.sk.S, content: Item.content.S });
});

3. When using queries or scans, include the user ID in the filter and avoid relying on client-supplied indexes for access control. For example, query posts by user and validate ownership on the server side.

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

app.get("/users/:userId/posts", async (req, res) => {
  const userId = req.user.sub;
  const command = new QueryCommand({
    TableName: process.env.POSTS_TABLE,
    IndexName: "userId-index",
    KeyConditionExpression: "userId = :uid",
    ExpressionAttributeValues: {
      ":uid": { S: userId }
    }
  });

  const { Items } = await ddb.send(command);
  const posts = Items.map(i => ({ postId: i.sk.S, content: i.content.S }));
  res.json(posts);
});

4. Avoid returning internal IDs directly to clients when possible, or transform them in a way that does not expose sequential or guessable values. If you must expose identifiers, consider hashing or namespacing them and maintain a server-side mapping in DynamoDB.

These patterns align with the checks MiddleBrick performs under its BOLA/IDOR and Property Authorization categories: validating that access patterns are scoped to the requester and that authorization is enforced at the data layer, not only at the endpoint.

Related CWEs: bolaAuthorization

CWE IDNameSeverity
CWE-250Execution with Unnecessary Privileges HIGH
CWE-639Insecure Direct Object Reference CRITICAL
CWE-732Incorrect Permission Assignment HIGH

Frequently Asked Questions

Can IDOR be detected by scanning unauthenticated endpoints only?
Yes. MiddleBrick’s unauthenticated scan surface can surface IDOR risks by probing known identifiers without credentials and correlating findings with the OpenAPI spec to detect missing ownership checks, even when authentication is optional or misconfigured.
Does fixing the route layer fully resolve DynamoDB IDOR risks?
Not alone. You must also enforce authorization in DynamoDB access patterns, avoid trusting client-supplied keys, scope queries to the requesting subject, and validate data ownership server-side. Defense in depth across the Express routes and DynamoDB design is required.