Time Of Check Time Of Use in Feathersjs with Basic Auth
Time Of Check Time Of Use in Feathersjs with Basic Auth — how this specific combination creates or exposes the vulnerability
Time Of Check Time Of Use (TOCTOU) is a class of race condition where a system checks a condition (such as permissions or authentication) and later relies on that condition without re-verifying it. In Feathersjs with Basic Auth, this occurs when authorization decisions are made before the request state is finalized, allowing an attacker to change the subject of authorization between the check and the use of the resource.
Consider a Feathers service that first authenticates a user via Basic Auth (supplying a username and base64-encoded credentials in the Authorization header), performs an access check (for example, confirming the user is an admin), and then proceeds to a data operation such as updating a record. If the framework or service code caches the user identity or role after the initial check and does not validate the credentials or permissions immediately before the operation, an attacker who can manipulate the session or request context might switch to another user or escalate privileges between the check and the use phase.
A concrete scenario: a route guarded by a custom hook that decodes the Basic Auth header, finds the user, and attaches it to the params object. Later in the service or another hook, the code assumes the user is still the same and authorized to act on a specific resource identified by an :id parameter. An attacker who can force the server to use a different connection or who exploits timing differences may cause the authorization check to apply to one identity while the operation executes under a different identity. This is especially relevant when multiple asynchronous hooks or promises introduce gaps between validation and execution, and when the state used for authorization is not re-validated immediately prior to the sensitive action.
Basic Auth exacerbates the risk because the credentials are sent with each request; if the server caches decoded identity without tying it cryptographically or transactionally to the actual credentials presented, an attacker who can influence the request lifecycle may exploit timing to use stale authorization. In distributed or async Feathers setups, where authorization checks happen in one hook and the data mutation in another, TOCTOU can allow unauthorized actions if the user context is mutable or replayed between check and use.
To detect this pattern with middleBrick, you would submit your Feathers API endpoint URL. The scanner runs unauthenticated black-box tests, including authorization and BOLA/IDOR checks, that can surface timing-related authorization gaps by observing whether resource access remains consistent across repeated and manipulated requests.
Basic Auth-Specific Remediation in Feathersjs — concrete code fixes
Remediation focuses on ensuring that authorization is re-validated immediately before any sensitive operation, and that the identity used for the decision is tightly bound to the credentials presented in the request. Do not rely on cached user objects across asynchronous hooks; instead, verify credentials or re-check permissions at the point of use.
Example of secure Basic Auth authentication in a Feathers before hook that decodes credentials and attaches a verified user record only for the duration of the request:
const { AuthenticationError } = require('@feathersjs/errors');
const basicAuth = require('basic-auth');
async function authenticateBasicAuth(hook) {
const credentials = basicAuth(hook.request);
if (!credentials || !credentials.name || !credentials.pass) {
throw new AuthenticationError('Missing credentials');
}
const user = await hook.app.service('users').find({
query: { username: credentials.name }
});
if (user.total === 0) {
throw new AuthenticationError('Invalid credentials');
}
const verifiedUser = user.data[0];
// Verify password synchronously or with a constant-time compare
const valid = await verifyPassword(credentials.pass, verifiedUser.passwordHash);
if (!valid) {
throw new AuthenticationError('Invalid credentials');
}
// Attach minimal verified context; do not cache across async gaps
hook.context.authUser = { id: verifiedUser.id, roles: verifiedUser.roles };
return hook;
}
Example of a service method that re-validates authorization immediately before the operation (e.g., updating a resource), avoiding reliance on a pre-checked user object:
class SecureItemsService {
async update(id, data, params) {
// Re-validate identity and permissions right before use
const authUser = params.context && params.context.authUser;
if (!authUser) {
throw new AuthenticationError('Unauthorized');
}
const item = await this.get(id, params);
if (!item) {
throw new NotFound('Item not found');
}
// Ensure the user is allowed to modify this specific item
if (!userCanModify(authUser, item)) {
throw new AuthenticationError('Insufficient permissions');
}
// Proceed with update using verified authUser and item ownership
return this.Model.findByIdAndUpdate(id, data, { new: true });
}
}
Additional concrete measures:
- In hooks, avoid attaching user data to a shared object that persists across async boundaries; instead, pass verified identifiers explicitly into service methods.
- Use constant-time comparison for password checks and prefer hashing with a strong adaptive function (e.g., bcrypt) rather than storing plaintext secrets.
- Structure hooks so that authentication is separate from authorization, and authorization is repeated in the service layer or immediately before database actions.
- Leverage middleware or hook wrappers to enforce that every data access re-derives the minimum required permissions from the current request credentials.
With middleBrick’s GitHub Action, you can add API security checks to your CI/CD pipeline and automatically fail builds if risk scores exceed your threshold, helping to catch regressions that might reintroduce TOCTOU or authorization gaps.