Data Exposure in Adonisjs with Dynamodb
Data Exposure in Adonisjs with Dynamodb — how this specific combination creates or exposes the vulnerability
AdonisJS, a Node.js web framework, often interacts with AWS databases such as DynamoDB. When developers construct DynamoDB operations using uncontrolled request data, they can inadvertently expose sensitive information or enable unauthorized data access. Data exposure in this context occurs when responses include fields that should remain private, such as internal identifiers, email addresses, or credential metadata, and when insufficient validation allows attackers to manipulate query parameters to retrieve other users' records.
The risk is amplified because DynamoDB is a NoSQL store with flexible schemas; application code must explicitly project only the intended attributes. If projection expressions are omitted or built from user input, broader sets of attributes can be returned. For example, a profile endpoint that uses user-supplied id to fetch a DynamoDB item might return the entire item, including fields like passwordHash or refreshToken if they exist in the table.
Additionally, AdonisJS route handlers that merge request parameters directly into DynamoDB API parameters can lead to inconsistent filtering. Consider a handler that uses a userId from authentication and a resourceId from request params to build a GetCommand. If the handler does not verify that the resourceId belongs to the authenticated userId, an attacker can iterate over IDs and access other users' data. This is a classic broken object level authorization (BOLA) pattern that maps directly into data exposure findings.
In DynamoDB, the use of scans instead of queries with proper filters also contributes to exposure. Scans read every item in a table and are inefficient, but more critically, if a scan is accidentally exposed through an unguarded endpoint, it can return the entire table. Even when queries are used, incomplete key condition expressions or missing filter expressions can result in more items than intended being returned to the client.
Real-world examples align with this pattern. Findings from scans often map to the OWASP API Top 10 category Broken Object Level Authorization and can intersect with Sensitive Data Exposure when personally identifiable information is returned without masking. In PCI-DSS and SOC2 contexts, any exposure of cardholder data or authentication secrets is critical. middleBrick detects such issues during black-box scanning by analyzing the unauthentinated attack surface and cross-referencing OpenAPI specifications with runtime responses to identify mismatches in expected versus returned data fields.
Dynamodb-Specific Remediation in Adonisjs — concrete code fixes
Remediation focuses on strict input validation, explicit attribute projection, and ensuring that authorization checks are performed before any DynamoDB operation. Always use parameterized queries and never construct expression strings from raw user input.
1. Use explicit projection to limit returned attributes
When retrieving items, specify only the attributes your client needs. This prevents accidental exposure of sensitive fields.
import { DynamoDB } from "@aws-sdk/client-dynamodb";
import { unmarshall } from "@aws-sdk/util-dynamodb";
const client = new DynamoDB({});
export async function getUserProfile(userId, requestedFields) {
// Validate requestedFields against an allowlist
const allowedFields = new Set(["username", "displayName", "avatarUrl"]);
const projectionFields = requestedFields.filter((f) => allowedFields.has(f));
const projectionExpression = projectionFields.length > 0
? `#usr, ${projectionFields.map((f) => `#${f}`).join(", ")}`
: "#usr";
const command = new GetCommand({
TableName: process.env.USERS_TABLE,
Key: {
userId: { S: userId },
},
ProjectionExpression: projectionExpression,
ExpressionAttributeNames: {
"#usr": "username"
// add other aliases as needed
},
});
const response = await client.send(command);
return response.Item ? unmarshall(response.Item) : null;
}
2. Enforce ownership checks before queries
Ensure that any query includes the authenticated user identifier as part of the key or filter, and never rely on client-supplied identifiers alone.
import { DynamoDB } from "@aws-sdk/client-dynamodb";
import { unmarshall } from "@aws-sdk/util-dynamodb";
const client = new DynamoDB({});
export async function getUserData(authUserId, resourceId) {
// Both keys must be validated to prevent IDOR
if (!resourceId || !authUserId) {
throw new Error("Missing parameters");
}
const command = new GetCommand({
TableName: process.env.USER_DATA_TABLE,
Key: {
ownerId: { S: authUserId },
resourceId: { S: resourceId },
},
});
const response = await client.send(command);
return response.Item ? unmarshall(response.Item) : null;
}
3. Avoid scans; prefer queries with KeyConditionExpression
Use queries with explicit key conditions instead of scans. If a scan is necessary for administrative purposes, ensure it is behind strict authentication and authorization controls and never exposed to public endpoints.
import { DynamoDB } from "@aws-sdk/client-dynamodb";
import { unmarshall } from "@aws-sdk/util-dynamodb";
const client = new DynamoDB({});
export async function listUserResources(userId) {
const command = new QueryCommand({
TableName: process.env.USER_RESOURCES_TABLE,
KeyConditionExpression: "ownerId = :uid",
ExpressionAttributeValues: {
":uid": { S: userId },
},
// Optional filter for additional constraints
FilterExpression: "status = :active",
ExpressionAttributeValues: {
...ExpressionAttributeValues,
":active": { S: "active" },
},
});
const response = await client.send(command);
return response.Items ? response.Items.map(unmarshall) : [];
}
4. Use middleware to normalize and validate input
In AdonisJS, leverage middleware to sanitize and validate IDs and query parameters before they reach route handlers. This reduces the chance of malformed or malicious input affecting DynamoDB operations.
import { schema } from "@ioc:Adonis/Core/Validator";
import { HttpContextContract } from "@ioc:Adonis/Core/HttpContext";
const resourceSchema = schema.create({
resourceId: schema.string({ trim: true, escape: true }),
});
export async function validateResourceId(ctx: HttpContextContract) {
const payload = await ctx.validate({
schema: resourceSchema,
});
return payload.resourceId;
}
5. Map findings to compliance and provide remediation guidance
middleBrick findings often reference OWASP API Top 10 and compliance frameworks. For each data exposure finding, remediation guidance includes: enforce least privilege, apply field-level masking, rotate exposed credentials, and update API specifications to reflect only necessary attributes. Continuous monitoring in the Pro plan can help detect regressions by scanning on a configurable schedule and alerting when new exposure patterns appear.
Related CWEs: dataExposure
| CWE ID | Name | Severity |
|---|---|---|
| CWE-200 | Exposure of Sensitive Information | HIGH |
| CWE-209 | Error Information Disclosure | MEDIUM |
| CWE-213 | Exposure of Sensitive Information Due to Incompatible Policies | HIGH |
| CWE-215 | Insertion of Sensitive Information Into Debugging Code | MEDIUM |
| CWE-312 | Cleartext Storage of Sensitive Information | HIGH |
| CWE-359 | Exposure of Private Personal Information (PII) | HIGH |
| CWE-522 | Insufficiently Protected Credentials | CRITICAL |
| CWE-532 | Insertion of Sensitive Information into Log File | MEDIUM |
| CWE-538 | Insertion of Sensitive Information into Externally-Accessible File | HIGH |
| CWE-540 | Inclusion of Sensitive Information in Source Code | HIGH |