Crlf Injection in Loopback with Basic Auth
Crlf Injection in Loopback with Basic Auth — how this specific combination creates or exposes the vulnerability
Crlf Injection occurs when user-controlled data is reflected in HTTP headers without sanitization, enabling an attacker to inject additional headers or split the response. In Loopback applications that use HTTP Basic Authentication via the built-in rest-security component, this risk is amplified when the framework copies values from authorization or request headers into downstream headers or response locations without validation.
Consider a Loopback endpoint that reads a custom header such as X-Request-ID and includes it in the HTTP response or passes it into an email or log header. If an unauthenticated attacker sends a request with a Authorization: Basic dXNlcjpwYXNz header and also provides X-Request-ID: abc\r\nSet-Cookie: injected=1, Loopback may reflect or forward the header value to other subsystems. Because the Basic Auth value itself is parsed by the framework before application logic runs, an attacker can craft a header line that terminates the current header and appends a new one, effectively smuggling a Set-Cookie or Location header into the response.
The specific combination of unauthenticated attack surface analysis and header parsing in Loopback means that even without credentials, an attacker can probe for header reflection points. The 12 security checks in middleBrick, including Input Validation and HTTP Response Smuggling checks, flag such patterns when a CRLF sequence (%0d%0a or \r\n) appears in header-derived values. In practice, this has been observed in APIs that concatenate Authorization-derived tokens or user IDs into Location or WWW-Authenticate headers, enabling response splitting or cache poisoning.
Because middleBrick scans unauthenticated attack surfaces, it can detect these injection points by sending raw \r\n sequences in header values and observing whether they appear reflected in subsequent responses. The scan’s Input Validation and Property Authorization checks highlight cases where user data reaches headers without canonicalization or strict allowlisting. This is distinct from authentication bypass; here the Basic Auth header provides a valid credential context that may be leveraged to contextualize injected header lines, increasing the potential impact in environments that rely on header-based routing or logging.
Real-world patterns include concatenating a user identifier extracted from the Basic Auth token into a Location header for redirects, or using the request’s authorization-derived tenant ID in a custom header that is later echoed back. In such cases, a payload like Authorization: Basic dXNlcjpwYXNz\r\nX-Injected: 1 can cause the server to treat the injected line as a separate header, leading to unintended behavior such as session fixation or information disclosure.
Basic Auth-Specific Remediation in Loopback — concrete code fixes
Remediation focuses on preventing untrusted data from reaching HTTP headers and ensuring strict parsing of authorization values. The following Loopback code examples illustrate secure handling when Basic Authentication is used.
First, avoid using raw header values in redirects or echoed headers. Instead of directly concatenating a user identifier from the Basic Auth token into a Location header, generate an opaque, server-side session or token. The example below shows a secure redirect approach that does not reflect attacker-controlled data:
const loopback = require('loopback');
const app = loopback();
app.use(loopback.token());
app.get('/secure-redirect', (req, res) => {
const auth = req.headers.authorization || '';
const isAuthenticated = validateBasicAuth(auth); // custom server-side validation
if (!isAuthenticated) {
return res.status(401).send('Unauthorized');
}
// Do NOT use user-provided data in redirects
const redirectUrl = '/dashboard'; // hardcoded or server-generated URL
return res.redirect(redirectUrl);
});
function validateBasicAuth(authHeader) {
if (!authHeader || !authHeader.startsWith('Basic ')) return false;
const base64 = authHeader.split(' ')[1];
const decoded = Buffer.from(base64, 'base64').toString('utf8');
const [user, pass] = decoded.split(':');
// Validate against a secure store; avoid reflecting user or password
return user === 'apiuser' && pass === 's3cureP@ss';
}
Second, sanitize any data that may be used in logging or error responses. If you must log authorization context, hash or truncate sensitive portions and never reflect raw header lines. The following snippet demonstrates safe logging without header injection risk:
const loopback = require('loopback');
const app = loopback();
app.use((req, res, next) => {
const auth = req.headers.authorization || '';
let userHash = 'unknown';
if (auth && auth.startsWith('Basic ')) {
try {
const base64 = auth.split(' ')[1];
const decoded = Buffer.from(base64, 'base64').toString('utf8');
const [user] = decoded.split(':');
userHash = require('crypto').createHash('sha256').update(user).digest('hex');
} catch (err) {
userHash = 'invalid';
}
}
req.auditUser = userHash;
next();
});
app.get('/profile', (req, res) => {
res.json({ userHash: req.auditUser, status: 'ok' });
});
Third, enforce strict header validation at the application or gateway layer. Use allowlists for known header names and reject any header values containing \r or \n. In Loopback, you can add a request preprocessor to sanitize incoming headers before they reach route handlers:
app.use((req, res, next) => {
const headers = req.headers;
for (const [key, value] of Object.entries(headers)) {
if (typeof value === 'string' && (value.includes('\r') || value.includes('\n'))) {
return res.status(400).send('Invalid header value');
}
}
next();
});
These patterns ensure that Basic Auth credentials are validated server-side and never used to construct or influence HTTP headers in a way that could enable Crlf Injection. By combining input validation, avoiding header reflection, and using server-side tokens for redirects, the attack surface is significantly reduced.