Open Redirect in Feathersjs with Basic Auth
Open Redirect in Feathersjs with Basic Auth — how this specific combination creates or exposes the vulnerability
An Open Redirect in a Feathersjs application using Basic Authentication can occur when redirect logic relies on user-controlled values without validation. Feathersjs is a framework-agnostic JavaScript API layer; when paired with a transport like Express or Koa, routes and hooks are defined explicitly. If a route or hook constructs a redirect URL using parameters such as req.query.next or req.body.returnUrl without strict allowlisting, an attacker can supply a malicious external URL.
Basic Authentication in Feathersjs is typically implemented via an authentication middleware that checks an Authorization header. When Basic Auth is enabled, the server decodes credentials from the header and attaches a user object to the request. An Open Redirect becomes more problematic in this context because authenticated requests carrying Basic Auth credentials can be manipulated to redirect to phishing sites. For example, an authenticated session might follow a crafted link like https://api.example.com/v1/after-login?next=https://evil.com, causing the server to redirect the authenticated user to the attacker’s domain.
The risk is amplified when redirect behavior is not confined to same-origin destinations. Feathersjs hooks that handle post-login flows or custom service methods that return redirect instructions may inadvertently trust input from query strings or request bodies. Because Basic Auth provides a persistent identity across requests, an attacker who tricks a victim into visiting a malicious link can leverage the victim’s credentials context to obscure the phishing nature of the redirect. Proper validation—such as ensuring redirect targets are relative paths or match a strict set of trusted hosts—is essential to mitigate this attack vector in Feathersjs applications using Basic Auth.
Basic Auth-Specific Remediation in Feathersjs — concrete code fixes
To remediate Open Redirect in Feathersjs with Basic Auth, validate and restrict redirect targets rigorously. Always use relative paths or explicitly check the host against a whitelist. Below are concrete examples demonstrating secure handling in a Feathersjs service.
Example 1: Safe redirect using relative paths
Instead of redirecting to a user-supplied URL, use a known relative path after authentication:
// src/hooks/redirect-hook.js
module.exports = function () {
return async context => {
const { params } = context;
// After successful authentication, redirect to a fixed route
if (params.redirect === 'dashboard') {
context.redirect = '/app/dashboard';
} else {
context.redirect = '/app/home';
}
return context;
};
};
Example 2: Validate against a trusted host allowlist
If a redirect is necessary, ensure the target URL is within a trusted set of domains:
// src/hooks/validate-redirect-hook.js
const allowedHosts = ['app.example.com', 'dashboard.example.com'];
module.exports = function () {
return async context => {
const { redirect } = context.params;
let targetUrl;
try {
targetUrl = new URL(redirect);
} catch (err) {
throw new Error('Invalid redirect URL');
}
if (!allowedHosts.includes(targetUrl.hostname)) {
throw new Error('Redirect target not allowed');
}
context.redirect = targetUrl.toString();
return context;
};
};
Example 3: Basic Auth authentication hook with safe continuation
This example shows a custom authentication hook that decodes Basic Auth and enforces a safe continuation path:
// src/hooks/basic-auth-hook.js
const { AuthenticationError } = require('@feathersjs/errors');
const auth = require('basic-auth');
module.exports = function (options = {}) {
return async context => {
const credentials = auth(context.headers);
if (!credentials || credentials.name !== 'user' || credentials.pass !== 'secret') {
throw new AuthenticationError('Invalid credentials');
}
// Attach user to context securely
context.params.user = { id: 1, username: credentials.name };
// Safe continuation: use a default route, ignore untrusted 'next' query param
const safeRedirect = '/profile';
context.redirect = safeRedirect;
return context;
};
};
Example 4: Service method returning a safe redirect URL
Within a Feathersjs service, ensure any returned redirect is constructed server-side:
// src/services/authentication/authentication.service.js
class AuthService {
async create(data, params) {
const { username, password } = data;
// Validate credentials (pseudo-validation)
if (username !== 'admin' || password !== 'secure') {
throw new Error('Invalid credentials');
}
// Server-defined redirect, never using user input directly
return {
redirectUrl: '/dashboard',
token: 'secure-jwt-token'
};
}
}
module.exports = function () {
const app = this;
app.use('/auth', new AuthService());
};
These patterns ensure that redirect behavior remains under server control, mitigating Open Redirect risks while using Basic Auth within Feathersjs. Always prefer relative paths or strictly validated absolute URLs.