Log Injection in Adonisjs with Dynamodb
Log Injection in Adonisjs with Dynamodb — how this specific combination creates or exposes the vulnerability
Log injection occurs when untrusted input is written directly into log entries without sanitization or formatting, enabling an attacker to forge log lines, obscure true events, or inject additional control characters that affect log interpretation. In an AdonisJS application using DynamoDB as a log store (for example, writing structured application events to a DynamoDB table), the risk arises when request data—such as user identifiers, query parameters, or request bodies—is concatenated into log messages before being stored.
AdonisJS does not inherently sanitize values that reach your logging layer; if you construct log entries by string interpolation and then push them to DynamoDB via the AWS SDK, newlines (%0A, %0D) and other control characters can break the expected schema of your log items. In DynamoDB, a single item can contain multiple lines within an attribute value, but downstream log aggregation tools that parse line-delimited streams may treat each injected line as a separate, potentially misleading log entry. This can complicate incident response and allow an attacker to inject fabricated timestamps, error levels, or context that obscures the true sequence of events.
Because DynamoDB is a NoSQL database, you store structured JSON documents. If log entries are stored as items with attributes like timestamp, level, and message, unsanitized input placed into message can introduce newline characters that disrupt parsers expecting single-line messages. Additionally, if you use CloudWatch Logs subscription filters or a custom pipeline to ship DynamoDB-stored logs to SIEMs, injected newlines may cause misalignment between log events, leading to false positives or missed detections. Attackers may also attempt to inject structured payloads (e.g., JSON fragments) that, when concatenated, alter the effective schema of your log items and complicate automated analysis.
To discover such issues, scanning an AdonisJS service that writes to DynamoDB with middleBrick is valuable: the tool exercises unauthenticated endpoints, inspects the OpenAPI contract, and checks whether log-related endpoints or debug routes expose user-controlled data in responses or side effects. middleBrick’s checks include input validation and data exposure, which can surface endpoints that leak sensitive information into logs or accept payloads that may be written to storage used for logging.
Dynamodb-Specific Remediation in Adonisjs — concrete code fixes
Remediation focuses on ensuring that log messages remain single-line, deterministic, and safely separable. You should sanitize and escape user-controlled data before it reaches the logger, and avoid embedding raw user input into log payloads that are stored in DynamoDB.
1. Sanitize and escape user input before logging
Create a small utility that strips or escapes newlines and other control characters from strings that will be written to log items. For example:
// utils/sanitizeLog.js
function sanitizeLog(value) {
if (typeof value !== 'string') return value;
// Replace newlines and carriage returns; replace non-printable control chars
return value.replace(/[\r\n\u0000-\u001F\u007F]/g, ' ').trim();
}
module.exports = { sanitizeLog };
Use this utility on any user-supplied fields before constructing the log item:
// controllers/SomeController.js
const { sanitizeLog } = use('App/Utils/sanitizeLog');
const { request } = use('request');
async store({ request, logger }) {
const payload = request.only(['search', 'filter']);
const safeSearch = sanitizeLog(payload.search);
const safeFilter = sanitizeLog(payload.filter);
logger.info('user_query', {
userId: request.$auth?.id,
search: safeSearch,
filter: safeFilter,
timestamp: new Date().toISOString()
});
// Continue with business logic
}
2. Use structured logging with a newline-safe format
When writing to DynamoDB, store logs as single-line JSON items. Avoid embedding newline characters in string attributes. Example DynamoDB put operation:
// services/LogService.js
const { DynamoDBDocumentClient, PutCommand } = require('@aws-sdk/lib-dynamodb');
const { dyanmodbConfig } = use('Config');
const client = DynamoDBDocumentClient.from(new dyanmodbConfig.AWS.DynamoDB({}));
async function writeLogItem(logItem) {
const params = {
TableName: process.env.LOG_TABLE_NAME,
Item: {
id: logItem.id || `log-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
timestamp: logItem.timestamp || new Date().toISOString(),
level: logItem.level || 'info',
message: logItem.message || '',
// Ensure message does not contain newlines
messageText: logItem.message ? logItem.message.replace(/[\r\n]+/g, ' ') : ''
}
};
await client.send(new PutCommand(params));
}
module.exports = { writeLogItem };
In your AdonisJS logger provider, plug this writer so that each log entry becomes a single DynamoDB item without embedded newlines inside user-controlled attributes.
3. Validate and constrain log inputs at the route level
Add validation rules to endpoints that could otherwise feed uncontrolled data into logs. For instance, ensure search parameters do not contain line breaks:
// validators/QueryValidator.js
const { schema } = use('@ioc:Adonis/Core/Validator');
const querySchema = schema.create({
search: schema.string.optional({}, [ rules.sanitize() ]),
filter: schema.string.optional({}, [ rules.sanitize() ])
});
module.exports = { querySchema };
Then in your controller, use the validated payload and pass sanitized values to the logger.
4. Ensure log shippers handle DynamoDB content safely
If you use a subscription or export pipeline to move logs from DynamoDB to another system, make sure the consumer normalizes line endings and treats each DynamoDB item as a discrete event rather than a stream of line-separated text. This prevents injected newlines from causing parsing errors or event duplication.