Excessive Data Exposure in Feathersjs with Basic Auth
Excessive Data Exposure in Feathersjs with Basic Auth
Excessive Data Exposure occurs when an API returns more data than the client is authorized to see. In Feathersjs applications that use Basic Auth without additional safeguards, this risk is pronounced because the framework’s default service configuration can expose entire database records, including fields that should be restricted. Basic Auth itself only transports credentials over the wire; it does not enforce field-level permissions. If service hooks or queries are not explicitly scoped, authenticated requests can receive sensitive fields such as password hashes, internal status flags, or PII that should remain hidden.
Feathersjs relies on a service-oriented architecture where each service defines how data is queried and returned. By default, a Feathers service will return the full database row for a given query unless you customize the paginate options or the before and after hooks. When Basic Auth is used, the user identity is typically attached to the request object via an authentication strategy, but developers must explicitly use that identity to filter what data is accessible. Without scoping queries to the authenticated user (for example, using user ID from params.user), an attacker who obtains a valid credential can enumerate or request records that belong to other users, leading to horizontal privilege escalation.
Consider a typical Feathers service definition for a user profile:
// services/users/users.js
const { Service } = require('feathersjs');
class UserService extends Service {
async find(params) {
// WARNING: returns all user fields including password and internal flags
return super.find(params);
}
async get(id, params) {
// WARNING: returns full user record including sensitive fields
return super.get(id, params);
}
}
module.exports = function (app) {
app.use('/users', new UserService({
Model: app.get('sequelize').models.users,
paginate: { default: 10, max: 50 }
}));
};
In this example, if Basic Auth validates a user but the service does not filter fields or scope queries, an authenticated request can retrieve another user’s record by guessing or iterating IDs. The response may include fields such as passwordHash, resetToken, or isAdmin, which constitute Excessive Data Exposure. This becomes critical when combined with weak access controls: an attacker can leverage a valid but low-privilege Basic Auth credential to harvest sensitive information across the system.
To quantify the risk, tools like middleBrick scan such endpoints and flag findings under the Data Exposure category, mapping them to frameworks like OWASP API Top 10 and GDPR. The scanner evaluates whether responses include sensitive fields and whether query scoping aligns with authenticated identity. For Basic Auth flows in Feathersjs, remediation centers on hook-based filtering and field selection to ensure least privilege.
Basic Auth-Specific Remediation in Feathersjs
Remediation focuses on ensuring that every query is scoped to the authenticated user and that responses exclude sensitive fields. In Feathersjs, this is achieved through service hooks that run before queries and get requests. You should attach the authenticated user’s ID from params.user (populated by the Basic Auth strategy) to the query filter, and explicitly define which fields are safe to return.
Below is a secure Feathers service example using Basic Auth with field filtering and user scoping:
// services/users/users.js
const { iff, isProvider } = require('feathers-hooks-common');
const { authenticate } = require('@feathersjs/authentication');
const { hashPassword, protect } = require('@feathersjs/authentication-local');
class UserService {
async before(hook) {
// Ensure the request is authenticated via Basic Auth
authenticate('local')(hook);
// Scope GET /users/:id to the authenticated user
if (hook.method === 'get' && hook.params.user) {
const userId = hook.params.user.id;
hook.params.query = { ...hook.params.query, _id: userId };
}
return hook;
}
async after(hook) {
// Remove sensitive fields from responses
const safeFields = ['_id', 'email', 'firstName', 'lastName', 'role'];
if (hook.result) {
if (Array.isArray(hook.result.data)) {
hook.result.data = hook.result.data.map(item => {
const filtered = {};
safeFields.forEach(field => {
if (item.hasOwnProperty(field)) filtered[field] = item[field];
});
return filtered;
});
} else {
const filtered = {};
safeFields.forEach(field => {
if (hook.result.hasOwnProperty(field)) filtered[field] = hook.result[field];
});
hook.result = filtered;
}
}
return hook;
}
}
module.exports = function () {
const app = this;
const options = {
Model: app.get('sequelize').models.users,
paginate: { default: 10, max: 50 },
hooks: {
before: new UserService().before,
after: new UserService().after
}
};
app.use('/users', new (class extends require('feathersjs').Service { constructor(props) { super(props); } })(options));
};
Key points in this remediation:
- Authentication hook ensures requests are validated via Basic Auth before processing.
- The
beforehook scopes queries by injecting the authenticated user’s ID into the query filter, preventing horizontal data access. - The
afterhook strips sensitive fields such as password hashes and internal flags, reducing the data footprint returned to the client.
Additionally, you should configure the Local authentication strategy to use Basic Auth headers securely, ensuring credentials are transmitted only over HTTPS. middleBrick’s scans can validate that these hooks are present and that responses do not include prohibited fields, aligning the implementation with compliance mappings for OWASP API Top 10 and SOC2 controls.
Related CWEs: propertyAuthorization
| CWE ID | Name | Severity |
|---|---|---|
| CWE-915 | Mass Assignment | HIGH |