Information Disclosure in Dynamodb
How Information Disclosure Manifests in Dynamodb
Information disclosure in Dynamodb often stems from misconfigured permissions and improper handling of error responses. When developers use overly permissive IAM roles or expose internal error messages, attackers can extract sensitive data without authentication.
One common pattern occurs when Dynamodb queries return entire items when a single attribute is requested. Consider this vulnerable code:
const params = {
TableName: 'Users',
Key: { userId: '123' }
};
dynamodb.get(params, (err, data) => {
if (err) {
console.error(err); // Logs full error object
return res.status(500).json(err);
}
res.json(data.Item); // Returns entire user object
});This exposes all attributes of the user record, including sensitive fields like passwords or SSNs that shouldn't be returned to the client.
Another frequent issue involves error message leakage. When Dynamodb operations fail due to permission issues, the error messages often contain table names, partition keys, or even partial data:
// Vulnerable error handling
const params = {
TableName: 'Secrets',
Key: { secretId: 'confidential' }
};
try {
const result = await dynamodb.get(params).promise();
return result.Item;
} catch (error) {
console.log(error.message); // Logs: "User: arn:aws:iam::123456789012:user/app not authorized to access Secrets"
throw error; // Returns detailed error to client
}Attackers can use these error messages to map your data structure and identify sensitive tables.
Scan operations without proper filters also cause information disclosure. A developer might write:
// Vulnerable scan operation
const params = {
TableName: 'Users',
ProjectionExpression: 'email' // Only email requested
};
const result = await dynamodb.scan(params).promise();
res.json(result.Items); // Returns full items with all attributesEven though only email was requested, Dynamodb returns the entire item because ProjectionExpression was ignored or misconfigured.
Dynamodb-Specific Detection
Detecting information disclosure in Dynamodb requires examining both IAM policies and application code. Start by reviewing IAM permissions using the AWS CLI:
aws iam get-policy-version \
--policy-arn arn:aws:iam::123456789012:policy/DynamoDBFullAccess \
--version-id v1Look for overly broad statements like "Resource: *" or missing conditions that restrict access to specific attributes.
For runtime detection, middleBrick's Dynamodb-specific scanning identifies information disclosure through several mechanisms:
Attribute Exposure Analysis: The scanner tests whether endpoints return more attributes than requested. It sends requests with specific ProjectionExpression values and verifies the response contains only those attributes.
Error Message Inspection: middleBrick captures and analyzes error responses for sensitive information leakage. It flags responses containing table names, partition keys, or IAM principal details.
Permission Escalation Testing: The scanner attempts operations with reduced permissions to see if error messages reveal internal structure. For example:
// What middleBrick tests for
const limitedParams = {
TableName: 'Users',
Key: { userId: 'test' },
ProjectionExpression: 'email'
};
try {
await dynamodb.get(limitedParams).promise();
} catch (error) {
// Check if error contains sensitive info
if (error.message.includes('Users') ||
error.message.includes('userId') ||
error.message.includes('arn:aws:iam')) {
// Information disclosure detected
}
}Scan Operation Monitoring: The scanner identifies endpoints that use scan operations without proper filters or limits, which can expose entire table contents.
middleBrick's LLM/AI security features also detect if your Dynamodb endpoints are exposed to AI services without proper authentication, preventing prompt injection attacks that could extract data through legitimate API calls.
Dynamodb-Specific Remediation
Remediating information disclosure in Dynamodb requires a defense-in-depth approach. Start with IAM policy hardening:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"dynamodb:GetItem",
"dynamodb:Query",
"dynamodb:Scan"
],
"Resource": "arn:aws:dynamodb:us-east-1:123456789012:table/Users",
"Condition": {
"ForAllValues:StringEquals": {
"dynamodb:Attributes": ["email", "name", "userId"]
}
}
}
]
}This policy restricts access to only specified attributes, preventing retrieval of sensitive fields even if the application code is vulnerable.
In your application code, implement strict attribute filtering:
const safeAttributes = ['email', 'name', 'userId'];
async function getUser(userId, requestedAttributes = safeAttributes) {
const params = {
TableName: 'Users',
Key: { userId },
ProjectionExpression: requestedAttributes.join(',')
};
try {
const result = await dynamodb.get(params).promise();
if (!result.Item) return null;
// Filter attributes server-side
const filteredItem = {};
requestedAttributes.forEach(attr => {
if (result.Item.hasOwnProperty(attr)) {
filteredItem[attr] = result.Item[attr];
}
});
return filteredItem;
} catch (error) {
// Log minimal error information
console.error('Dynamodb error:', error.code || 'Unknown error');
throw new Error('Internal server error');
}
}Implement proper error handling that never exposes internal details:
try {
const result = await dynamodb.query(params).promise();
return result.Items;
} catch (error) {
// Map specific errors to generic messages
const errorMap = {
'AccessDeniedException': 'Permission error',
'ResourceNotFoundException': 'Resource not found',
'ValidationException': 'Invalid request'
};
const userMessage = errorMap[error.code] || 'Internal server error';
console.error('Dynamodb operation failed:', {
code: error.code,
endpoint: req.originalUrl,
timestamp: new Date().toISOString()
});
throw new Error(userMessage);
}Use Dynamodb's built-in encryption for sensitive data:
const params = {
TableName: 'Users',
Key: { userId },
ProjectionExpression: 'email, name',
ConsistentRead: true,
ReturnConsumedCapacity: 'TOTAL'
};
// Enable SSE for the table
// aws dynamodb update-table \
// --table-name Users \
// --sse-specification Enabled=trueFinally, implement rate limiting and request validation to prevent enumeration attacks:
const rateLimiter = new RateLimiterMemory({
points: 100, // 100 requests
duration: 60, // per 60 seconds
blockDuration: 300 // block for 5 minutes if exceeded
});
app.get('/api/users/:userId', rateLimiter.middleware({
keyGenerator: (req) => req.user.id || req.ip
}), async (req, res) => {
// Your handler code
});