Crlf Injection in Express with Bearer Tokens
Crlf Injection in Express with Bearer Tokens — how this specific combination creates or exposes the vulnerability
Crlf Injection occurs when an attacker can inject carriage return (CR, \r) and line feed (\n) characters into a header or status-line context. In Express, this commonly arises when dynamic values are concatenated directly into response headers without sanitization. When Bearer Tokens are handled in Express routes—such as extracting a token from the Authorization header and using it to construct downstream requests or custom response headers—the risk of Crlf Injection increases if the token or any related value is reflected into headers without validation.
Consider an Express route that copies an incoming Bearer Token into a custom response header for debugging or logging:
const express = require('express');
const app = express();
app.get('/callback', (req, res) => {
const auth = req.headers['authorization']; // "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6..."
if (auth && auth.startsWith('Bearer ')) {
const token = auth.slice(7);
// Potential Crlf Injection if token contains \r or \n
res.set('X-Auth-Token', token);
res.send('ok');
} else {
res.status(401).send('missing bearer');
}
});
app.listen(3000);
If an attacker sends a request like Authorization: Bearer aaa\r\nX-Admin: true, the injected CRLF can cause X-Auth-Token to be split, smuggling a second header X-Admin: true into the response. This can bypass intended access controls or mislead downstream logging and monitoring. The same risk exists when tokens are used to build URLs or query parameters for external HTTP calls, especially if the token is later reflected into a header or log entry that an attacker can influence.
Another scenario involves using Bearer Tokens in authorization flows where the token value is included in a Location header during redirects. For example:
app.get('/login', (req, res) => {
const token = req.query.token; // untokenized, user-controlled
res.redirect(`/dashboard?token=${token}`);
});
While this example focuses on query parameters, if the token were instead placed into a header-based redirect (e.g., via a custom Authorization manipulation), CRLF characters in the token could lead to response splitting. Even when tokens are validated for format, many libraries do not explicitly reject CR or LF characters, so attackers can exploit this gap.
The impact can include HTTP response splitting, session fixation, or cache poisoning, depending on how the tainted token is used. Because Bearer Tokens often carry high privilege, ensuring they are treated as untrusted input is essential. middleBrick scans for such header injection risks by correlating OpenAPI specs with runtime behavior, flagging places where untrusted data reaches headers without proper sanitization.
Bearer Tokens-Specific Remediation in Express — concrete code fixes
Remediation centers on strict validation, canonicalization, and avoiding the direct reflection of Bearer Tokens into headers or status lines. Always treat the contents of Authorization headers as opaque strings; do not parse or repurpose them beyond authentication checks. Use hardened libraries for header setting and redirect construction, and enforce strict character policies on any derived values.
1. Avoid reflecting Bearer Tokens into headers. Do not copy raw token values into response headers. If you must associate a token with a response, store a salted hash or a short-lived session identifier instead:
const crypto = require('crypto');
const express = require('express');
const app = express();
function hashToken(token) {
return crypto.createHash('sha256').update(token).digest('hex');
}
app.get('/callback', (req, res) => {
const auth = req.headers['authorization'];
if (auth && auth.startsWith('Bearer ')) {
const token = auth.slice(7);
// Store a hash, not the raw token
res.set('X-Auth-Token-SHA256', hashToken(token));
res.send('ok');
} else {
res.status(401).send('missing bearer');
}
});
app.listen(3000);
2. Sanitize all inputs used in redirects and headers. If you use token-derived values in redirects or new headers, strip or reject CR and LF characters explicitly. Use a library or a simple regex to enforce safe characters:
const express = require('express');
const app = express();
function safeHeaderValue(value) {
if (value == null) return '';
if (/[\r\n]/.test(value)) {
throw new Error('Invalid header value: contains CR or LF');
}
return value;
}
app.get('/profile', (req, res) => {
const auth = req.headers['authorization'];
if (auth && auth.startsWith('Bearer ')) {
const token = auth.slice(7);
try {
const safe = safeHeaderValue(token);
res.set('X-Token-Preview', safe.slice(0, 8));
} catch (err) {
return res.status(400).send('invalid token format');
}
res.send('profile');
} else {
res.status(401).send('missing bearer');
}
});
app.listen(3000);
3. Use built-in Express APIs for redirects. When constructing redirects, prefer Express’s res.redirect() with a plain path or a fully validated absolute URL. Avoid inserting untrusted tokens into the URL fragment or query string if they will later be reflected into headers. If you must pass tokens, use HttpOnly, Secure cookies with SameSite attributes rather than URL or header tokens:
const express = require('express');
const app = express();
app.get('/login', (req, res) => {
const token = req.query.token;
if (!token) return res.status(400).send('token required');
// Validate token format before use
if (!/^[A-Za-z0-9\-_=]+\.[A-Za-z0-9\-_=]+\.?[A-Za-z0-9\-_=]*$/.test(token)) {
return res.status(400).send('invalid token');
}
// Set as cookie instead of URL or header
res.cookie('token', token, { httpOnly: true, secure: true, sameSite: 'lax' });
res.redirect('/dashboard');
});
app.listen(3000);
These practices reduce the attack surface by ensuring Bearer Tokens never directly manipulate header boundaries. middleBrick’s scans can verify that endpoints do not reflect raw tokens into headers and that CRLF characters are rejected where inappropriate, supporting compliance with OWASP API Top 10 and related standards.