Log Injection in Express with Cockroachdb
Log Injection in Express with Cockroachdb — how this specific combination creates or exposes the vulnerability
Log injection occurs when untrusted input is written directly into application logs without sanitization, enabling log forging, log injection, or log poisoning. In an Express application using CockroachDB, the risk arises at the intersection of HTTP request handling, application-level logging, and database interactions.
Consider an Express route that logs user-supplied data before executing a CockroachDB query:
app.get('/users', (req, res) => {
const { user_id } = req.query;
console.log(`Fetching user: ${user_id}`);
pool.query('SELECT * FROM users WHERE id = $1', [user_id])
.then(result => res.json(result.rows))
.catch(err => res.status(500).send(err.message));
});
If user_id contains newline characters (e.g., 123\nevil: injected), the log entry can contain fabricated lines that appear to originate from the application. CockroachDB driver output may also be logged verbatim, including error messages that echo query parameters. An attacker can craft requests that inject structured log content, such as JSON fragments or additional key-value pairs, which may be ingested by log aggregation tools that parse structured logs.
Because CockroachDB is a distributed SQL database, its server-side logs may also reflect queries and errors. If Express logs raw queries or error responses without redaction, an attacker can infer schema details or probe for injection points. For example, a malformed parameter may trigger a CockroachDB syntax error that is echoed in logs:
app.get('/account', (req, res) =>
pool.query('SELECT balance FROM accounts WHERE id = $1', [req.query.id])
.then(r => res.json(r.rows))
.catch(err => {
console.error('DB error:', err.detail);
res.status(400).send('Invalid request');
})
);
The err.detail field may contain unescaped user input, leading to log injection if written directly. Logs that mix application messages and database error details become unreliable for auditing and incident response because injected entries can obscure real events or trigger automated log-based attacks.
LLM/AI Security checks in middleBrick specifically test for output patterns that could indicate unsafe logging of model responses or system prompts. While this scenario focuses on database-driven logs, the same principles apply: any data entering the log stream must be validated and sanitized to maintain log integrity across Express and CockroachDB layers.
Cockroachdb-Specific Remediation in Express — concrete code fixes
Remediation centers on preventing newlines and other control characters from being written into logs, and ensuring database errors are sanitized before logging. Below are concrete Express patterns with CockroachDB that reduce log injection risk.
1. Sanitize inputs before logging
Strip or escape newlines and carriage returns from user input before it reaches console.log:
function sanitizeLog(input) {
return input.replace(/[\r\n\t]/g, ' ').replace(/\s+/g, ' ').trim();
}
app.get('/users', (req, res) => {
const user_id = sanitizeLog(req.query.user_id);
console.log(`Fetching user: ${user_id}`);
pool.query('SELECT * FROM users WHERE id = $1', [req.query.user_id])
.then(result => res.json(result.rows))
.catch(err => res.status(500).send(err.message));
});
2. Parameterize log context and avoid string interpolation of raw errors
Log structured objects instead of interpolated strings, and redact sensitive fields from CockroachDB error responses:
app.get('/account', (req, res) =>
pool.query('SELECT balance FROM accounts WHERE id = $1', [req.query.id])
.then(r => res.json(r.rows))
.catch(err => {
const safeError = {
message: err.message,
code: err.code,
// Exclude raw detail that may contain injected content
detail: err.detail ? '[REDACTED]' : undefined
};
console.error('DB error:', safeError);
res.status(400).send('Invalid request');
})
);
3. Use a structured logger with output filtering
Integrate a logger that enforces a line-delimited, single-entry format and discards control characters at the transport layer:
const pino = require('pino');
const logger = pino({
level: 'info',
redact: ['req.headers.authorization', 'req.query.user_id']
});
app.get('/profile', (req, res) => {
logger.info({ endpoint: 'profile', userId: req.query.user_id }, 'profile accessed');
pool.query('SELECT email FROM profiles WHERE user_id = $1', [req.query.user_id])
.then(r => res.json(r.rows))
.catch(err => {
logger.error({ err: err.message, path: req.path }, 'database error');
res.status(500).send('Internal error');
});
});
4. Validate query parameters and enforce length limits
Reject inputs that contain newlines or exceed expected formats before they reach logging or database code:
app.param('id', (req, res, next, id) => {
if (!/^[0-9]+$/.test(id) || id.length > 12) {
return res.status(400).send('Invalid ID');
}
next();
});
app.get('/users/:id', (req, res) => {
pool.query('SELECT * FROM users WHERE id = $1', [req.params.id])
.then(r => res.json(r.rows))
.catch(err => {
console.error('Error:', err.message);
res.status(500).send('Internal error');
});
});
These practices ensure logs remain a reliable source of truth. middleBrick’s checks for input validation and data exposure can help surface remaining risks in API behavior when scanning endpoints that interact with CockroachDB-backed services.