Header Injection in Feathersjs with Basic Auth
Header Injection in Feathersjs with Basic Auth — how this specific combination creates or exposes the vulnerability
Header Injection occurs when untrusted input is reflected into HTTP response headers without validation or sanitization. In Feathersjs applications that use Basic Authentication, this risk is amplified because the framework’s hook and service architecture can inadvertently pass attacker-controlled data into headers that are later sent to the client or downstream services.
Basic Auth typically relies on the Authorization header, which contains a base64-encoded username:password string. Feathersjs does not inherently validate or sanitize values that developers explicitly set on the request object (e.g., via hooks) before they are used to construct response headers. If a hook or service reads a user-supplied value (such as a username, token, or query parameter) and assigns it to a header like x-requested-user or x-forwarded-user, an attacker can inject newline characters (CRLF, %0d%0a or \r\n) to split the header and inject additional headers.
This becomes particularly dangerous when Feathersjs is used with custom header-based routing, logging, or integration layers. An injected header such as X-Account-ID: 12345 or a set-cookie directive can alter the behavior of clients or intermediaries. For example, an attacker might inject Set-Cookie: session=attacker to manipulate client-side session handling, or inject headers that bypass certain security policies enforced by proxies or API gateways.
Because Feathersjs often serves as a backend for SPAs and mobile clients, and because Basic Auth credentials are transmitted on every request, any header injection vector can lead to HTTP response splitting, cache poisoning, or client-side deception. The risk is not theoretical: newline characters in usernames or custom header values are common root causes. Even if the application does not directly set headers, improperly sanitized input that flows through hooks into logging or monitoring integrations may be reflected in error messages or custom headers added by middleware.
In a black-box scan using middleBrick, this issue would be surfaced under the Data Exposure and Input Validation checks, with evidence showing that attacker-supplied newlines are reflected in response headers. The scanner also checks for SSRF and unsafe consumption patterns, but header injection is specifically flagged when user-controlled data reaches HTTP response construction in an unescaped form.
Basic Auth-Specific Remediation in Feathersjs — concrete code fixes
Remediation focuses on strict input validation, avoiding the reflection of untrusted data into headers, and ensuring that Basic Auth credentials are handled only by the framework or standard libraries.
1. Do not reflect untrusted input into headers
Never take values from request parameters, body, or query strings and directly assign them to headers. If you need to enrich request context, use the Feathers request object (params) internally without exporting it to headers.
// ❌ Dangerous: reflects user input into a custom header
app.hooks({
before: {
create: [context => {
const { email } = context.data;
// Risk: attacker sends \r\n via email to inject headers
context.params.headers['x-email'] = email;
return context;
}]
}
});
// ✅ Safe: do not reflect raw user input into headers
app.hooks({
before: {
create: [context => {
// Use a sanitized identifier or omit the header entirely
context.params.internalUserId = context.result ? context.result.userId : null;
return context;
}]
}
});
2. Validate and sanitize any custom headers you must set
If you must set custom headers, validate them strictly and disallow CR/LF characters. Use a dedicated sanitization library or simple regex checks.
// ✅ Safe: sanitize header values
function sanitizeHeader(value) {
if (typeof value !== 'string') return '';
return value.replace(/[\r\n]+/g, '');
}
app.hooks({
before: {
create: [context => {
const safeUser = sanitizeHeader(context.params.user);
if (safeUser) {
context.params.headers['x-safe-user'] = safeUser;
}
return context;
}]
}
});
3. Use Feathers authentication hooks correctly with Basic Auth
When using Basic Auth, rely on established libraries for credential parsing and avoid manually decoding or forwarding the Authorization header. Do not echo the decoded user fields into response headers.
// ✅ Safe: use a standard auth hook and avoid header echoing
const authentication = require('@feathersjs/authentication');
const local = require('@feathersjs/authentication-local');
app.configure(authentication({
secret: process.env.AUTH_SECRET,
strategies: ['jwt', 'local']
}));
app.use('/authentication', local({
usernameField: 'email',
passwordField: 'password'
}));
// Custom hook that does not reflect Basic Auth fields into response headers
app.hooks({
after: {
create: [context => {
// Do not set headers based on context.params.headers provided by client
// Instead, attach internal metadata only
context.params.internalMeta = {
authenticated: true,
userId: context.result ? context.result.userId : null
};
return context;
}]
}
});
4. Harden server and framework defaults
Ensure your HTTP server layer (e.g., Express adapter used by Feathers) does not permit header injection by design. Avoid adding custom headers based on request content, and review any third-party Feathers plugins that may manipulate headers. middleBrick scans will highlight unauthenticated endpoints and input validation gaps, helping you identify places where headers might be influenced by client data.
Finally, prefer token-based authentication (e.g., JWT) over Basic Auth where feasible, as it reduces the risk of credential exposure and simplifies header management. If you must use Basic Auth, ensure credentials are transmitted only over TLS and never reflected into mutable headers.