Man In The Middle in Feathersjs with Basic Auth
Man In The Middle in Feathersjs with Basic Auth — how this specific combination creates or exposes the vulnerability
FeathersJS is a framework for creating JavaScript APIs and applications. When Basic Authentication is used without transport-layer protection, the credentials are transmitted as a base64-encoded string in the Authorization header. Base64 is not encryption; it is easily reversible. A Man In The Middle (MitM) attacker who can observe or intercept network traffic can decode the credentials and gain unauthorized access.
In a FeathersJS application, the typical integration of Basic Auth via an authentication hook sends the credentials in every request. If the endpoint is served over HTTP instead of HTTPS, or if HTTPS is misconfigured (for example, using weak ciphers or accepting deprecated protocols), an attacker on the same network segment or path can perform session hijacking. The attacker might exploit unencrypted Wi‑Fi, a compromised router, or a vulnerable proxy to capture the traffic. Because Basic Auth does not include inherent integrity protection, the credentials can be altered in transit, enabling request tampering.
Moreover, FeathersJS applications that rely solely on Basic Auth without additional mechanisms (such as CSRF tokens or secure cookie attributes) can be susceptible to credential replay. An intercepted Authorization header can be reused to impersonate a user until the credentials are changed. This is particularly dangerous in microservice environments where service-to-service communication might inadvertently rely on unauthenticated or weakly authenticated HTTP channels.
To illustrate, consider a FeathersJS service that uses the @feathersjs/authentication and @feathersjs/authentication-local packages. If the transport is not enforced as HTTPS, the Basic Auth credentials passed during login can be captured. The attacker does not need to break the authentication logic; they only need to observe the traffic. This highlights the necessity of enforcing HTTPS for any deployment that uses Basic Auth, combined with strict transport security policies such as HTTP Strict Transport Security (HSTS).
Finally, the combination of FeathersJS and Basic Auth requires careful attention to the scope and lifetime of tokens. If access tokens derived from Basic Auth are long-lived or improperly scoped, a captured token extends the window of exposure. Mitigations include short-lived tokens, secure token storage, and continuous monitoring of authentication patterns to detect anomalies indicative of MitM activity.
Basic Auth-Specific Remediation in Feathersjs — concrete code fixes
Remediation focuses on ensuring that Basic Auth credentials are never transmitted or stored in the clear and that the transport is always authenticated and encrypted. The primary fix is to enforce HTTPS across the entire application and to avoid using Basic Auth over HTTP.
Enforce HTTPS in FeathersJS
Configure your server to accept only HTTPS connections. Below is an example using the built-in HTTPS server in Node.js with a valid certificate and key:
const https = require('https');
const fs = require('fs');
const feathers = require('@feathersjs/feathers');
const express = require('@feathersjs/express');
const app = express(feathers());
const options = {
key: fs.readFileSync('/path/to/private-key.pem'),
cert: fs.readFileSync('/path/to/certificate.pem'),
ca: fs.readFileSync('/path/to/ca-bundle.pem')
};
https.createServer(options, app).listen(443, () => {
console.log('HTTPS server running on port 443');
});
Secure Basic Auth with HTTPS and Environment Variables
Ensure that the username and password used for Basic Auth are stored in environment variables and never hard-coded. Then configure the authentication hook to use secure transport:
const authentication = require('@feathersjs/authentication');
const local = require('@feathersjs/authentication-local');
app.configure(authentication({
entity: 'user',
service: 'users',
secret: process.env.AUTH_SECRET,
authStrategies: ['local']
}));
app.configure(local());
app.use('/authentication', authentication());
// Example of a hook that ensures secure headers and rejects non-HTTPS requests
app.hooks({
before: {
all: [context => {
if (context.params.provider === 'rest' && !context.params.secure) {
throw new Error('HTTPS required');
}
return context;
}]
}
});
Implement Strict Transport Security
Use HTTP Strict Transport Security (HSTS) headers to instruct browsers to only use HTTPS:
const helmet = require('helmet');
app.use(helmet.hsts({
maxAge: 31536000,
includeSubDomains: true,
preload: true
}));
Avoid Sending Sensitive Data in URLs
Ensure that credentials are not passed as query parameters, which can be logged in server logs or browser history. Always use the Authorization header over HTTPS.
Rotate Credentials and Use Short-Lived Tokens
While Basic Auth typically relies on static username/password pairs, consider augmenting with token-based flows where feasible. If using tokens derived from Basic Auth, enforce short lifetimes and refresh mechanisms.