Log Injection in Express with Bearer Tokens
Log Injection in Express with Bearer Tokens — how this specific combination creates or exposes the vulnerability
Log injection occurs when untrusted input is written directly into log files without proper sanitization or formatting, allowing an attacker to forge log entries, inject newlines, or hide malicious activity. In Express applications that use Bearer token authentication, the combination of token-based auth and insufficient log handling creates specific risks.
When an Express backend validates a Bearer token (typically via the Authorization header), the raw token value or its parsed claims might be logged for debugging or audit purposes. If the token or related request data is written into logs verbatim — for example, using console.log or a basic logger without escaping — an attacker can supply a crafted token containing newline characters (\n) or structured payloads. This enables log injection, where the attacker can insert fake entries, impersonate other users, or break log parsers that rely on line-based formats.
Consider an Express route that logs authentication events:
app.get('/profile', (req, res) => {
const token = req.headers.authorization?.split(' ')[1];
console.log('Auth success: token=' + token);
res.json({ ok: true });
});
If an attacker sends Authorization: Bearer "attacker\n[CRITICAL] admin=true", the log file may show two separate lines, making it appear that a second, legitimate authentication event occurred. This can obscure real intrusions or trigger incorrect alerts in monitoring tools. Moreover, if logs are later analyzed by security tooling that expects well-formed entries, injected content can bypass detection rules or create false positives.
Log injection in this context also intersects with token leakage. Should logs inadvertently capture full tokens due to verbose error messages or misconfigured debug output, the confidentiality of those tokens is compromised. Tokens embedded in logs may persist in storage longer than necessary and could be exposed if log access controls are weak. Because Bearer tokens are high-value secrets, any log entry that reveals them increases the impact of a log injection or log exposure vulnerability.
Additionally, certain logging formats — such as JSON-structured logging — can be abused if user-controlled fields are not validated. An attacker might supply a token containing characters that break JSON syntax or field delimiters, leading to parsing failures or ambiguous records. The interplay between the Express request pipeline, where headers are mutable, and logging implementations that trust incoming data, makes this a practical and material threat.
Bearer Tokens-Specific Remediation in Express — concrete code fixes
Remediation focuses on preventing untrusted data from corrupting log entries and ensuring tokens are handled safely. The primary rule is to never directly concatenate or interpolate raw token values into logs. Instead, sanitize and abstract identifiers before logging.
1. Avoid logging raw tokens. Replace direct logging with a hashed or truncated representation:
const crypto = require('crypto');
function hashToken(token) {
return crypto.createHash('sha256').update(token).digest('hex').substring(0, 16);
}
app.get('/profile', (req, res) => {
const authHeader = req.headers.authorization || '';
const token = authHeader.startsWith('Bearer ') ? authHeader.slice(7) : '';
if (!token) return res.status(401).json({ error: 'missing_token' });
// Safe logging: never log the raw token
const tokenFingerprint = hashToken(token);
console.log('Auth success: token_fingerprint=' + tokenFingerprint);
res.json({ ok: true });
});
2. Use structured logging with explicit fields and avoid string concatenation that may be affected by newline characters in token values. With a JSON logger, ensure user-controlled values are not placed in fields that affect log structure:
const winston = require('winston');
const logger = winston.createLogger({
level: 'info',
format: winston.format.json(),
transports: [new winston.transports.Console()],
});
app.get('/profile', (req, res) => {
const authHeader = req.headers.authorization || '';
const token = authHeader.startsWith('Bearer ') ? authHeader.slice(7) : '';
if (!token) return res.status(401).json({ error: 'missing_token' });
// Structured log with controlled fields; token not included
logger.info('authentication_success', {
endpoint: '/profile',
token_fingerprint: hashToken(token),
ip: req.ip,
method: req.method,
});
res.json({ ok: true });
});
3. Validate token format before use to reduce the risk of injection via malformed input. Reject tokens that contain suspicious characters or lengths that do not match your expected pattern:
function isValidToken(token) {
// Basic Bearer token sanity checks
return typeof token === 'string' && /^[A-Za-z0-9\-._~+/]+={0,2}$/.test(token) && token.length <= 4096;
}
app.get('/profile', (req, res) => {
const authHeader = req.headers.authorization || '';
const parts = authHeader.split(' ');
if (parts.length !== 2 || parts[0] !== 'Bearer') {
return res.status(400).json({ error: 'bad_auth_header_format' });
}
const token = parts[1];
if (!isValidToken(token)) {
return res.status(400).json({ error: 'invalid_token' });
}
const tokenFingerprint = hashToken(token);
console.log('Auth success: token_fingerprint=' + tokenFingerprint);
res.json({ ok: true });
});
4. Ensure error handling and debug outputs do not leak tokens. Avoid sending full authorization headers in error responses or logs:
app.use((err, req, res, next) => {
// Do not include raw token in error responses
const authHeader = req.headers.authorization || '';
const token = authHeader.startsWith('Bearer ') ? '[protected]' : '[none]';
logger.error('server_error', {
error: err.message,
token_placeholder: token,
endpoint: req.originalUrl,
});
res.status(500).json({ error: 'internal_server_error' });
});
These practices reduce the attack surface by ensuring tokens are never written raw into logs, log structure remains intact, and suspicious inputs are rejected early. Combined with transport-layer protections and proper access controls on log storage, they help mitigate both log injection and inadvertent token exposure in Express services using Bearer tokens.