Cors Wildcard in Express with Dynamodb
Cors Wildcard in Express with Dynamodb — how this specific combination creates or exposes the vulnerability
Using a CORS wildcard (Access-Control-Allow-Origin: *) in an Express application that interacts with DynamoDB can unintentionally expose sensitive data and amplify authorization issues. When the wildcard is set at the Express/CORS layer, browsers allow any origin to read responses from your endpoints. If those endpoints return DynamoDB data without enforcing per-request authorization, any webpage on the internet can make authenticated requests on behalf of a user (BOLA/IDOR) and harvest data that should be restricted.
Consider an Express route that queries DynamoDB based on a path parameter such as userId:
app.get('/users/:userId', async (req, res) => {
const { userId } = req.params;
const params = {
TableName: 'users',
Key: { userId },
};
const data = await docClient.get(params).promise();
res.json(data.Item);
});
If the route is protected only by a CORS wildcard and not by proper ownership checks, a malicious site can load this endpoint for any userId and read other users' records. This becomes a data exposure vector when combined with DynamoDB’s behavior: successful GetItem responses return the item if the key exists, even if the requester should not have access to that specific partition key. The wildcard does not cause the authorization flaw, but it removes the browser-enforced same-origin policy that would otherwise limit which origins can observe leaked data.
The risk is especially relevant when the API exposes sensitive attributes (email, PII, tokens) and when preflight requests are handled permissively. An attacker can chain CORS wildcard misconfiguration with DynamoDB queries that lack attribute-level or row-level authorization, leading to mass data extraction. This pattern is commonly flagged as a high-severity finding in scans because it maps directly to OWASP API Top 10:2023 — Broken Object Level Authorization (BOLA) and Data Exposure.
In a middleBrick scan, such a setup typically results in a high severity finding under BOLA/IDOR and Data Exposure categories, with remediation guidance focused on replacing the wildcard with specific origins and enforcing ownership checks on every DynamoDB request.
Dynamodb-Specific Remediation in Express — concrete code fixes
To remediate, enforce strict CORS origins and implement per-request authorization that validates the authenticated subject against the DynamoDB partition key. Below is a secure Express pattern that combines proper CORS configuration with DynamoDB access control.
1) Configure CORS to specify origins instead of using a wildcard. For production, avoid * and enumerate allowed origins:
const cors = require('cors');
const allowedOrigins = ['https://app.example.com', 'https://admin.example.com'];
app.use(cors({
origin: (origin, callback) => {
if (!origin || allowedOrigins.includes(origin)) {
callback(null, true);
} else {
callback(new Error('Not allowed by CORS'));
}
},
credentials: true,
}));
2) Enforce ownership checks in your route handler. Retrieve the authenticated user’s ID (e.g., from a session or JWT) and ensure it matches the DynamoDB partition key:
app.get('/users/:userId', async (req, res) => {
const requestingUserId = req.user.sub; // from auth middleware
const targetUserId = req.params.userId;
if (requestingUserId !== targetUserId) {
return res.status(403).json({ error: 'Forbidden: cannot access other user data' });
}
const params = {
TableName: 'users',
Key: { userId: targetUserId },
// Project only necessary attributes to minimize exposure
ProjectionExpression: 'userId,email,role',
};
try {
const data = await docClient.get(params).promise();
if (!data.Item) {
return res.status(404).json({ error: 'Not found' });
}
res.json(data.Item);
} catch (err) {
res.status(500).json({ error: 'Internal server error' });
}
});
3) Apply a DynamoDB condition expression when updates are allowed, ensuring the item’s partition key matches the subject. This prevents BOLA even if a higher-level check is bypassed:
app.put('/users/:userId', async (req, res) => {
const requestingUserId = req.user.sub;
const targetUserId = req.params.userId;
const updateParams = {
TableName: 'users',
Key: { userId: targetUserId },
UpdateExpression: 'set email = :email',
ConditionExpression: 'userId = :uid',
ExpressionAttributeValues: {
':email': req.body.email,
':uid': targetUserId,
},
ReturnValues: 'UPDATED_NEW',
};
try {
await docClient.update(updateParams).promise();
res.json({ message: 'updated' });
} catch (err) {
if (err.code === 'ConditionalCheckFailedException') {
return res.status(403).json({ error: 'Cannot update this resource' });
}
res.status(500).json({ error: 'Internal server error' });
}
});
These steps ensure that CORS does not widen the attack surface and that DynamoDB operations remain constrained to the authenticated user’s data. middleBrick will flag the wildcard CORS configuration and missing ownership checks separately, enabling you to address each control with clearly separated remediation actions.
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 |