Format String in Express with Bearer Tokens
Format String in Express with Bearer Tokens — how this specific combination creates or exposes the vulnerability
A format string vulnerability occurs when user-controlled input is passed directly to a function that interprets format specifiers (e.g., printf-style behavior) without proper sanitization. In Express, this typically manifests when logging, error reporting, or string construction uses unchecked input in format placeholders such as %s, %d, or %j. When combined with Bearer Tokens, the risk surface expands because tokens are often logged, parsed, or echoed in responses for debugging or auditing purposes.
Consider an Express route that extracts a token from the Authorization header and passes it directly to a logging function using a format string:
const express = require('express');
const app = express();
app.get('/profile', (req, res) => {
const authHeader = req.headers['authorization'] || '';
const token = authHeader.startsWith('Bearer ') ? authHeader.slice(7) : null;
// Vulnerable: token is used directly in a format string
console.log('User token: %s', token);
res.json({ ok: true });
});
app.listen(3000);
If an attacker sends a crafted request with format specifiers in the token (e.g., Bearer %s%d%j), the console.log call may interpret and expand these specifiers using subsequent arguments or internal state, potentially causing information disclosure, crashes, or in some environments, code execution. In a Bearer Token context, this can lead to token leakage through log files or console output, especially when combined with verbose format patterns.
Another scenario involves response formatting where user-controlled data is interpolated into a template or log entry using format-style concatenation. For example:
app.get('/data', (req, res) => {
const token = extractBearerToken(req);
// Unsafe string construction that may be interpreted as a format string
const message = 'Token: %s'.replace('%s', token);
console.log(message);
res.send('ok');
});
Although JavaScript’s console.log does not natively support printf-style formatting, certain libraries or custom logging wrappers might. If a logging utility internally uses functions like util.format, an attacker-supplied token containing percent signs can trigger unintended behavior, such as reading stack variables or injecting additional output. This is particularly dangerous when tokens are logged in environments where log files are later parsed or displayed to users.
SSRF and external interaction risks can also emerge if token-related logs are sent to external endpoints or if format string behavior interacts with downstream parsers. While the primary concern here is information disclosure, the broader impact includes loss of confidentiality and potential escalation when tokens are mishandled in formatted output.
Bearer Tokens-Specific Remediation in Express — concrete code fixes
To mitigate format string risks when handling Bearer Tokens in Express, ensure that token values are never directly embedded into format strings or logging calls that interpret format specifiers. Use safe stringification methods and structured logging instead.
1. Validate and sanitize the Authorization header before any processing:
function extractBearerToken(req) {
const authHeader = req.headers['authorization'] || '';
const match = authHeader.match(/^Bearer \s*(\S+)$/i);
return match ? match[1] : null;
}
2. Use safe logging that avoids format-style interpolation:
const util = require('util');
app.get('/profile', (req, res) => {
const token = extractBearerToken(req);
// Safe: no format specifiers in user input
console.log('User token:', token);
res.json({ ok: true });
});
3. If structured logging is required, use an object-based approach instead of string formatting:
const logger = {
info: (obj) => console.log(JSON.stringify(obj))
};
app.get('/data', (req, res) => {
const token = extractBearerToken(req);
logger.info({ event: 'token_access', has_token: !!token });
res.json({ ok: true });
});
4. For applications using templating or message formatting libraries, ensure that token values are passed as data fields rather than format arguments:
const template = (msg, data) => {
return msg.replace(/\$\{(\w+)\}/g, (_, key) => data[key] || '');
};
const safeMessage = template('Token status: ${status}', { status: 'present' });
console.log(safeMessage);
5. Enforce strict header validation and reject malformed Authorization values to reduce edge cases:
app.use((req, res, next) => {
const token = extractBearerToken(req);
if (token && token.length > 400) { // reasonable upper bound
return res.status(400).json({ error: 'invalid_token' });
}
next();
});
By combining careful parsing, avoiding format-style string construction with user input, and adopting structured logging, you reduce the risk of format string issues while still handling Bearer Tokens securely in Express.