Log Injection in Dynamodb
How Log Injection Manifests in Dynamodb
Log injection in Dynamodb environments occurs when untrusted data is written to logs without proper sanitization, allowing attackers to manipulate log files and potentially trigger unintended behavior in monitoring systems, SIEMs, or log-processing pipelines. The problem is particularly acute in Dynamodb applications because of the way error messages, query parameters, and database responses are often logged without considering injection vectors.
A common Dynamodb log injection scenario involves DynamoDB API errors. When a PutItem, UpdateItem, or Query operation fails, applications frequently log the entire error object or the raw parameters that caused the failure. Consider this vulnerable pattern:
const params = {
TableName: 'users',
Item: {
id: userId,
data: userData
}
};
try {
await dynamodb.put(params).promise();
} catch (err) {
console.log(`DynamoDB Error: ${err.message} - Params: ${JSON.stringify(params)}`);
// Attacker can inject: userId = '1'; console.log('INJECTED'); //
}
The JSON.stringify output creates a structured log entry, but if the logging system processes these logs with command-line tools or structured parsers, the injected content could be interpreted as commands or alter the log structure.
Another Dynamodb-specific vector involves conditional expression injection. When logging failed conditional checks:
const params = {
TableName: 'orders',
Key: { orderId: '123' },
UpdateExpression: 'set #status = :status',
ConditionExpression: 'attribute_not_exists(#status) OR #status = :currentStatus',
ExpressionAttributeNames: { '#status': 'status' },
ExpressionAttributeValues: {
':status': 'shipped',
':currentStatus': 'pending'
}
};
try {
await dynamodb.update(params).promise();
} catch (err) {
console.log(`Update failed: ${err.message} - Condition: ${params.ConditionExpression}`);
// Attacker controls :currentStatus value in params
}
If the ConditionExpression contains user-controlled values and is logged directly, an attacker could craft expressions that break the log format or inject additional content visible to downstream systems.
BatchWriteItem operations present another vulnerability. When logging failed batch operations, applications often log the entire requestItems object:
const params = {
RequestItems: {
'products': [
{ PutRequest: { Item: product1 } },
{ PutRequest: { Item: product2 } }
]
}
};
try {
await dynamodb.batchWrite(params).promise();
} catch (err) {
console.error(`Batch write failed: ${err.message}`);
console.log(`Failed items: ${JSON.stringify(params.RequestItems)}`);
// Large payloads can overwhelm log systems
}
The RequestItems structure can contain thousands of items, and logging the entire structure can cause log injection through size-based attacks or by including malicious content in the item attributes that gets processed by log aggregation systems.
Query and Scan operations with complex filter expressions also create injection opportunities. When logging query failures with user-controlled filter values:
const params = {
TableName: 'products',
FilterExpression: 'contains(#name, :search) OR contains(#description, :search)',
ExpressionAttributeNames: { '#name': 'name', '#description': 'description' },
ExpressionAttributeValues: { ':search': userInput }
};
try {
const data = await dynamodb.scan(params).promise();
} catch (err) {
console.log(`Scan error for search: ${userInput} - Message: ${err.message}`);
// If userInput contains special characters or control sequences
}
The FilterExpression and ExpressionAttributeValues can be manipulated to create log entries that break parsers or trigger unintended behavior in log analysis tools.
Dynamodb-Specific Detection
Detecting log injection vulnerabilities in Dynamodb applications requires both static code analysis and runtime scanning. middleBrick's black-box scanning approach is particularly effective for this because it can test the unauthenticated attack surface without requiring access to source code or credentials.
When scanning Dynamodb endpoints, middleBrick tests for log injection by sending specially crafted payloads through the API and monitoring how they appear in the application's logging behavior. The scanner looks for several Dynamodb-specific patterns:
API Error Response Analysis - middleBrick sends requests that trigger DynamoDB API errors (invalid parameters, missing required fields, type mismatches) and analyzes the error responses and any logged content. The scanner checks if error messages include user-controlled data in a way that could be exploited.
Conditional Expression Testing - The scanner crafts requests with malicious ConditionExpression values to see if they're reflected in logs or error messages. This includes testing for classic injection patterns like quotes, semicolons, and control characters.
Batch Operation Stress Testing - middleBrick sends large BatchWriteItem and BatchGetItem requests to test how the application handles and logs large payloads. This helps identify potential log injection through size-based attacks or malformed data structures.
Expression Attribute Injection - The scanner tests whether ExpressionAttributeValues containing special characters are properly sanitized when logged. This includes testing for newline characters, tabs, and other control sequences that could break log formatting.
Query Parameter Manipulation - For applications that log query parameters or filter values, middleBrick tests how user-controlled search terms are handled in Scan and Query operations. This includes testing for injection of special characters that could alter log structure.
middleBrick's DynamoDB-specific checks include scanning for:
| Check Type | What It Tests | Potential Impact |
|---|---|---|
| Error Message Logging | Whether DynamoDB error messages include user-controlled data | Information disclosure, log injection |
| Conditional Expression Logging | How failed conditional expressions are logged | Log structure manipulation |
| Batch Operation Logging | Whether large batch operations are logged in full | Log flooding, performance issues |
| Expression Attribute Handling | How ExpressionAttributeValues are processed in logs | Format breaking, injection |
| Query Parameter Reflection | Whether search/filter parameters appear in logs | Information leakage, injection |
The scanner provides specific findings with severity levels and remediation guidance. For example, a finding might indicate that error messages from PutItem operations include the full parameters object, allowing an attacker to inject arbitrary content into application logs.
middleBrick's continuous monitoring feature can also detect if log injection vulnerabilities are introduced in new deployments by automatically re-scanning APIs on a configurable schedule and alerting if security scores drop.
Dynamodb-Specific Remediation
Remediating log injection vulnerabilities in Dynamodb applications requires a combination of input validation, proper error handling, and careful logging practices. Here are specific remediation techniques for Dynamodb-related log injection issues:
Sanitize User Input Before Logging - Always sanitize user-controlled data before logging. For Dynamodb applications, this means validating and escaping data that comes from API requests, query parameters, and request bodies:
function sanitizeForLog(input) {
if (typeof input !== 'string') return input;
return input
.replace(/\n/g, '\n')
.replace(/\r/g, '\r')
.replace(/\t/g, '\t')
.replace(/\0/g, '\0')
.replace(//g, '>');
}
// Usage in error handling
const params = {
TableName: 'users',
Key: { id: userId }
};
try {
await dynamodb.get(params).promise();
} catch (err) {
console.error(`DynamoDB GetItem failed: ${err.message} - UserID: ${sanitizeForLog(userId)}`);
}
Structured Logging with Field Separation - Use structured logging that separates user data from log metadata. Instead of string interpolation, use JSON objects or logging libraries that handle field separation:
const logData = {
operation: 'PutItem',
table: params.TableName,
userId: userId,
error: err.message,
timestamp: new Date().toISOString()
};
// Use a structured logger
logger.error('DynamoDB operation failed', logData);
Limit Logged Data in Error Messages - Never log the full parameters object or request items in error messages. Instead, log only the essential information:
const MAX_LOGGED_PARAMS = 100;
function safeLogParams(params) {
if (!params) return null;
const safeParams = {};
for (const [key, value] of Object.entries(params)) {
if (key === 'ExpressionAttributeValues' || key === 'ExpressionAttributeNames') {
// Don't log expression attributes - they may contain sensitive data
continue;
}
if (typeof value === 'object') {
safeParams[key] = JSON.stringify(value).substring(0, MAX_LOGGED_PARAMS);
} else {
safeParams[key] = String(value).substring(0, MAX_LOGGED_PARAMS);
}
}
return safeParams;
}
// In error handling
catch (err) {
const safeParams = safeLogParams(params);
console.error(`DynamoDB error: ${err.message}`, { safeParams, timestamp: Date.now() });
}
Validate Conditional Expressions - Before executing Dynamodb operations with user-controlled conditional expressions, validate the structure and content:
function validateConditionExpression(expr) {
// Allow only alphanumeric, underscores, and basic operators
const allowedPattern = /^[a-zA-Z0-9_\s()=!<>]+$/;
return allowedPattern.test(expr);
}
// Usage
const userInputCondition = req.body.condition;
if (!validateConditionExpression(userInputCondition)) {
throw new Error('Invalid condition expression');
}
const params = {
TableName: 'orders',
Key: { orderId: req.params.id },
UpdateExpression: 'set #status = :status',
ConditionExpression: userInputCondition,
ExpressionAttributeNames: { '#status': 'status' },
ExpressionAttributeValues: { ':status': 'shipped' }
};
Implement Rate Limiting on Logging - Prevent log flooding attacks by implementing rate limiting on error logging:
class ErrorRateLimiter {
constructor(maxErrors, windowMs) {
this.maxErrors = maxErrors;
this.windowMs = windowMs;
this.errorCounts = new Map();
}
shouldLog(key) {
const now = Date.now();
const windowStart = now - this.windowMs;
// Clean old entries
for (const [k, v] of this.errorCounts.entries()) {
if (v.timestamp < windowStart) {
this.errorCounts.delete(k);
}
}
const current = this.errorCounts.get(key) || { count: 0, timestamp: now };
if (current.count >= this.maxErrors) {
return false;
}
current.count++;
this.errorCounts.set(key, current);
return true;
}
}
const rateLimiter = new ErrorRateLimiter(10, 60000); // 10 errors per minute
// In error handling
catch (err) {
const logKey = `${params.TableName}:${userId}`;
if (rateLimiter.shouldLog(logKey)) {
console.error(`DynamoDB error: ${err.message}`, { userId });
}
}
Log Masking for Sensitive Data - Implement log masking for sensitive fields that might appear in Dynamodb items:
function maskSensitiveData(item, sensitiveFields) {
const masked = { ...item };
for (const field of sensitiveFields) {
if (field in masked) {
const value = String(masked[field]);
if (value.length > 4) {
masked[field] = value.slice(0, 2) + '*'.repeat(value.length - 4) + value.slice(-2);
} else {
masked[field] = '*'.repeat(value.length);
}
}
}
return masked;
}
// Usage when logging items
const sensitiveFields = ['ssn', 'creditCard', 'password'];
const safeItem = maskSensitiveData(item, sensitiveFields);
console.log(`Processing item: ${JSON.stringify(safeItem)}`);
These remediation techniques, combined with middleBrick's scanning capabilities, provide comprehensive protection against log injection vulnerabilities in Dynamodb applications. The key is to implement defense-in-depth: validate inputs, sanitize outputs, use structured logging, and continuously monitor for new vulnerabilities.