HIGH prototype pollutionexpressdynamodb

Prototype Pollution in Express with Dynamodb

Prototype Pollution in Express with Dynamodb — how this specific combination creates or exposes the vulnerability

Prototype pollution in Express applications that interact with DynamoDB can occur when user-controlled input is merged into objects used to construct request parameters before the data is passed to DynamoDB operations. In JavaScript, objects inherit properties from their prototype chain; if an attacker can add or modify properties on Object.prototype or other shared prototypes, these properties can appear across many objects and affect behavior unexpectedly.

When an Express route builds a DynamoDB command (e.g., using the AWS SDK for JavaScript v3, PutCommand or UpdateCommand) by shallowly copying or mutating a request body into a params object, unsanitized keys can overwrite existing constructor or prototype properties. For example, if the code does Object.assign(params, body) or a custom merge where __proto__, constructor, or prototype keys are not filtered, they may alter how objects behave later in the request lifecycle or in downstream logic. Since DynamoDB operations rely on correctly shaped params (e.g., TableName, Key, Item), prototype pollution can indirectly cause malformed payloads, unexpected type coercions, or insecure defaults when the params are serialized or interpreted by other libraries.

An attacker might send a payload like { "__proto__": { "isAdmin": true } } or { "constructor": { "prototype": { "polluted": true } } }. If the Express route merges this into a DynamoDB params object, the pollution may not directly modify the DynamoDB request, but it can affect validation, serialization, or business logic that runs before constructing the request. For instance, a later JSON serialization or a schema validation library might read inherited properties, leading to privilege escalation or bypass of authorization checks. Moreover, if the params object is reused across multiple operations or cached, polluted prototypes can persist and affect subsequent requests, creating a server-side issue even though DynamoDB itself stores only the intended data attributes.

In the context of DynamoDB, it is critical to ensure that user input used to build Key condition expressions or update expressions does not rely on prototype-sensitive operations. For example, using Object.create(null) to create a dictionary without a prototype or deeply filtering input before constructing command parameters reduces the risk. The Express layer should treat any user input destined for DynamoDB as untrusted and validate/sanitize it according to the expected schema, rather than relying on the runtime’s default object behavior.

Dynamodb-Specific Remediation in Express — concrete code fixes

To prevent prototype pollution in Express when working with DynamoDB, isolate user input from prototype chain behavior and validate all fields used to construct DynamoDB commands. Below are concrete, safe patterns using the AWS SDK for JavaScript v3 with Express.

1. Use a prototype-free object for params

Create command parameters from a clean object that has no prototype. This prevents inherited properties from affecting behavior.

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

app.post("/items", (req, res) => {
  const cleanItem = Object.create(null);
  // Explicitly copy only allowed, validated fields
  if (typeof req.body.itemId === "string") cleanItem.itemId = { S: req.body.itemId };
  if (typeof req.body.value === "string") cleanItem.value = { S: req.body.value };
  // Ensure no prototype pollution keys reach the command
  const command = new PutCommand({
    TableName: "ItemsTable",
    Item: cleanItem
  });
  client.send(command).then(() => res.sendStatus(200));
});

2. Deep merge with prototype-safe merging libraries

If you must merge user input into an existing params template, use a merge that ignores __proto__, constructor, and prototype. For example, with a custom merge or a library configured to be safe:

function safeMerge(target, source) {
  if (!source || typeof source !== "object") return target;
  Object.keys(source).forEach((key) => {
    if (key === "__proto__" || key === "constructor" || key === "prototype") return;
    target[key] = source[key];
  });
  return target;
}

app.put("/items/:id", (req, res) => {
  const base = { TableName: "ItemsTable", Key: { id: { S: req.params.id } } };
  const updateExpressionParts = [];
  const expressionAttrValues = Object.create(null);
  if (typeof req.body.status === "string") {
    updateExpressionParts.push("set #s = :val");
    expressionAttrValues[":val"] = { S: req.body.status };
  }
  const params = safeMerge({ UpdateExpression: updateExpressionParts.join(", ") }, base);
  params.ExpressionAttributeValues = expressionAttrValues;
  const command = new UpdateCommand(params);
  client.send(command).then(() => res.sendStatus(200));
});

3. Validate and whitelist fields for DynamoDB commands

Only allow known fields for each DynamoDB operation. Do not pass through keys like __proto__, constructor, or prototype. Explicitly map validated input to DynamoDB attribute values.

app.post("/users", (req, res) => {
  const { username, email } = req.body;
  if (typeof username !== "string" || typeof email !== "string") {
    return res.sendStatus(400);
  }
  const command = new PutCommand({
    TableName: "UsersTable",
    Item: {
      username: { S: username },
      email: { S: email },
      // Avoid any dynamic key names derived from user input
    }
  });
  client.send(command).then(() => res.sendStatus(201));
});

4. Avoid unsafe object manipulation before constructing commands

Do not use functions like _.merge (without configuration) or spread syntax on user objects directly when building DynamoDB params. Instead, explicitly construct the shape you expect.

// Unsafe: can include prototype properties if user input is an object with __proto__
// const unsafeParams = { ...req.body };

// Safe: explicitly pick fields
const safeParams = {
  TableName: "OrdersTable",
  Item: {
    orderId: { S: req.body.orderId },
    amount: { N: String(req.body.amount) }
  }
};
const command = new PutCommand(safeParams);
client.send(command);

These patterns ensure that prototype pollution cannot influence DynamoDB command construction in Express. Combine this with input validation and schema checks to further reduce risk.

Frequently Asked Questions

Can prototype pollution affect DynamoDB stored data directly?
Prototype pollution typically affects JavaScript object behavior at runtime rather than altering how DynamoDB stores attributes. However, it can influence the params used to build requests, potentially causing malformed commands or bypassing application-level checks before data is written.
Does middleBrick detect prototype pollution risks in API specs and runtime?
middleBrick scans unauthenticated attack surfaces and includes checks that can surface insecure input handling and potential injection or pollution patterns. Findings include severity and remediation guidance mapped to frameworks such as OWASP API Top 10.