Insufficient Logging in Dynamodb
How Insufficient Logging Manifests in Dynamodb
Insufficient logging in DynamoDB environments creates blind spots that attackers exploit to maintain persistence and evade detection. When DynamoDB operations lack proper audit trails, critical security events go unnoticed.
Consider a DynamoDB table storing user sessions. Without logging, an attacker who successfully escalates privileges can iterate through all session tokens without leaving evidence. The following code demonstrates a common anti-pattern:
// ANTI-PATTERN: No logging of sensitive operations
async function getSessionData(userId) {
const params = {
TableName: 'UserSessions',
Key: { userId }
};
return await dynamodb.get(params).promise();
}This function retrieves session data without any audit trail. An attacker with compromised credentials could repeatedly call this function to harvest session tokens, and the lack of logging means the abuse goes undetected.
Another manifestation occurs during DynamoDB table scans. Attackers often use scan operations to discover table structures and data patterns. Without logging these operations, the reconnaissance phase remains invisible:
// ANTI-PATTERN: Unlogged scan operations
async function discoverTableStructure(tableName) {
const params = {
TableName: tableName,
Limit: 100
};
return await dynamodb.scan(params).promise();
}API Gateway integrations with DynamoDB present additional logging challenges. When API Gateway proxies directly to DynamoDB without proper logging, failed authentication attempts and suspicious query patterns go unmonitored:
// ANTI-PATTERN: Missing audit logging in API Gateway
export const handler = async (event: APIGatewayProxyEvent) => {
const userId = event.requestContext.authorizer?.principalId;
const params = JSON.parse(event.body);
// No logging of who accessed what data
const result = await dynamodb.put(params).promise();
}Time-based attacks also exploit insufficient logging. An attacker can perform operations during off-peak hours, knowing that the lack of continuous monitoring means abuse won't be detected until it's too late.
Dynamodb-Specific Detection
Detecting insufficient logging in DynamoDB requires examining both the database configuration and application code. AWS CloudTrail provides foundational logging, but it's often misconfigured or insufficient for detailed security analysis.
First, verify DynamoDB table-level logging is enabled. Each table should have CloudWatch Logs configured to capture:
- Read operations (GetItem, Query, Scan)
- Write operations (PutItem, UpdateItem, DeleteItem)
- Metadata changes (CreateTable, DeleteTable, UpdateTable)
- Access patterns from different IP addresses
middleBrick's DynamoDB security scanner specifically tests for these logging gaps. The scanner attempts operations that should trigger logs and verifies whether those logs exist in CloudWatch. For example, it tests whether unauthorized access attempts are logged:
# middleBrick scan output showing logging gaps
$ middlebrick scan https://api.example.com/dynamodb
Security Risk: C (73/100)
--- Insufficient Logging ---
❌ No audit trail for read operations
❌ Write operations not logged with user context
❌ Missing metadata change logging
❌ No IP-based access pattern loggingCode analysis reveals logging anti-patterns. middleBrick's static analysis identifies functions that perform sensitive operations without corresponding log statements:
// middleBrick identifies this as a security finding
async function updateUserProfile(userId, profileData) {
const params = {
TableName: 'Users',
Key: { userId },
UpdateExpression: 'set #name = :n, #email = :e',
ExpressionAttributeNames: { '#name': 'name', '#email': 'email' },
ExpressionAttributeValues: { ':n': profileData.name, ':e': profileData.email }
};
// middleBrick flags missing logging
}middleBrick also tests for proper error logging. Insufficient logging often means errors are swallowed or logged at inappropriate levels:
// middleBrick detects inadequate error handling
export const handler = async (event: APIGatewayProxyEvent) => {
try {
const result = await dynamodb.query(params).promise();
return { statusCode: 200, body: JSON.stringify(result) };
} catch (error) {
// ANTI-PATTERN: Error logged at wrong level
console.log('Query failed:', error);
return { statusCode: 500, body: 'Internal Server Error' };
}
}middleBrick's runtime scanning verifies that logging configurations exist and are properly structured. It checks for the presence of correlation IDs, user context, and operation metadata in log entries.
Dynamodb-Specific Remediation
Remediating insufficient logging in DynamoDB requires implementing comprehensive audit trails at both the infrastructure and application levels. Start with DynamoDB table configuration:
// PROPER LOGGING: Comprehensive DynamoDB operations
import { v4 as uuidv4 } from 'uuid';
import { CloudWatchLogger } from './cloudwatch-logger';
const logger = new CloudWatchLogger('DynamoDB-Audit');
export class AuditedDynamoDB {
constructor(private dynamodb: AWS.DynamoDB) {}
async getWithAudit(params: AWS.DynamoDB.GetItemInput, context: AuditContext) {
const correlationId = uuidv4();
const startTime = Date.now();
try {
const result = await this.dynamodb.get(params).promise();
correlationId,
userId: context.userId,
operation: 'GET',
tableName: params.TableName,
key: JSON.stringify(params.Key),
duration: Date.now() - startTime,
resultCount: result.Item ? 1 : 0,
ipAddress: context.ipAddress,
userAgent: context.userAgent
});
return result;
} catch (error) {
logger.error('GET_OPERATION_FAILED', {
correlationId,
userId: context.userId,
operation: 'GET',
tableName: params.TableName,
key: JSON.stringify(params.Key),
error: error.message,
stack: error.stack
});
throw error;
}
}
}Implement middleware for API Gateway integrations to ensure all DynamoDB operations are logged:
// PROPER LOGGING: API Gateway middleware
export const auditMiddleware = (handler: APIGatewayProxyHandler) => {
return async (event: APIGatewayProxyEvent) => {
const correlationId = uuidv4();
const startTime = Date.now();
const userId = event.requestContext.authorizer?.principalId;
const ipAddress = event.requestContext.identity.sourceIp;
try {
const result = await handler(event);
correlationId,
userId,
operation: event.httpMethod,
path: event.path,
duration: Date.now() - startTime,
ipAddress,
userAgent: event.headers['User-Agent']
});
return result;
} catch (error) {
logger.error('API_OPERATION_FAILED', {
correlationId,
userId,
operation: event.httpMethod,
path: event.path,
error: error.message,
stack: error.stack,
ipAddress
});
throw error;
}
}
}Configure CloudWatch Logs for DynamoDB table monitoring:
// PROPER LOGGING: CloudWatch configuration
export const configureDynamoDBLogging = (tableName: string) => {
const logGroupName = `/aws/dynamodb/${tableName}`;
// Create log group if it doesn't exist
const existingGroups = await cloudwatchlogs.describeLogGroups({ logGroupNamePrefix: logGroupName }).promise();
if (existingGroups.logGroups.length === 0) {
await cloudwatchlogs.createLogGroup({ logGroupName }).promise();
}
// Enable detailed monitoring
await dynamodb.updateTable({
TableName: tableName,
BillingMode: 'PAY_PER_REQUEST',
SSESpecification: { Enabled: true }
}).promise();
}For Lambda functions accessing DynamoDB, implement structured logging with correlation IDs:
// PROPER LOGGING: Lambda with structured logging
export const handler = async (event: APIGatewayProxyEvent) => {
const correlationId = uuidv4();
const startTime = Date.now();
try {
const userId = event.requestContext.authorizer?.principalId;
const params = JSON.parse(event.body);
// Audited DynamoDB operation
const result = await auditedDynamoDB.putWithAudit({
TableName: 'UserSessions',
Item: {
userId,
sessionId: uuidv4(),
data: params.data,
expiresAt: new Date(Date.now() + 24 * 60 * 60 * 1000).toISOString()
}
}, { userId, ipAddress: event.requestContext.identity.sourceIp });
logger.info('SESSION_CREATED', {
correlationId,
userId,
sessionId: result.Attributes.sessionId,
duration: Date.now() - startTime,
ipAddress: event.requestContext.identity.sourceIp
});
return { statusCode: 201, body: JSON.stringify(result) };
} catch (error) {
logger.error('SESSION_CREATION_FAILED', {
correlationId,
error: error.message,
stack: error.stack
});
return { statusCode: 500, body: 'Internal Server Error' };
}
}middleBrick's CLI tool can verify your logging implementation:
# middleBrick audit logging implementation
$ middlebrick audit-logging dynamodb --table UserSessions --endpoint https://api.example.com/dynamodb
Logging Audit Results:
✓ CloudWatch Logs configured ✓
✓ Structured logging with correlation IDs ✓
✓ Error logging at appropriate levels ✓
✓ User context included in logs ✓
✓ Audit trail for all CRUD operations ✓